This commit is contained in:
2026-04-30 10:25:34 +08:00
parent eebb0804b0
commit c1acba7196
22 changed files with 868 additions and 76 deletions

View File

@@ -15,7 +15,7 @@ VUE_APP_BASE_API = ''
PUBLIC_PATH = ''
# 二级部署路径
VUE_APP_APP_NAME ='yudao-admin'
# VUE_APP_APP_NAME ='yudao-admin'
# 多租户的开关
VUE_APP_TENANT_ENABLE = true

View File

@@ -81,13 +81,9 @@
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
<div class="el-upload__tip text-center" slot="tip">
<span>仅允许导入xlsxlsx格式文件</span>
<span>仅允许导入{{timeType==='month'?'月':'年'}}预算仅允许导入xlsxlsx格式文件</span>
</div>
<div class="el-upload__tip" slot="tip">
<el-radio-group v-model="upload.timeDim">
<el-radio :label="2">月预算</el-radio>
<el-radio :label="3">年预算</el-radio>
</el-radio-group>
</div>
</el-upload>
<div v-if="upload.httpUploading" class="upload-progress-wrap">
@@ -143,7 +139,6 @@ export default {
title: "预算填报导入",
fileList:[],
currentFile:null,
timeDim: 2,
// HTTP 上传中(点击确定后 axios 上传,展示不确定进度条)
httpUploading: false
},
@@ -530,7 +525,7 @@ export default {
try {
const formData = new FormData()
formData.append('file', this.upload.currentFile) // 文件字段
formData.append('timeDim', this.upload.timeDim) // 年月维度字段
formData.append('timeDim', this.timeType === 'month'?2:3) // 年月维度字段
formData.append('reportDate', this.form.endTime) // 时间维度字段
formData.append('levelId', this.form.levelId) // 层级维度字段
const response = await axios({
@@ -549,7 +544,6 @@ export default {
this.$message.success('文件上传成功!')
// 重置表单
this.upload.fileList = []
this.upload.timeDim = 2
this.upload.currentFile = null
this.upload.open = false
this.$refs.upload.clearFiles();

View File

@@ -156,30 +156,6 @@ export default {
}
},
series: [
{
name: '实际',
type: 'line',
// stack: 'Total', // 趋势图通常不需要堆叠
symbol: 'circle',
symbolSize: 8,
lineStyle: {
color: 'rgba(255, 132, 0, 1)', // 加深颜色
width: 2,
},
itemStyle: {
color: 'rgba(255, 132, 0, 1)',
borderColor: '#fff',
borderWidth: 2,
},
areaStyle: {
opacity: 0.3,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(255, 132, 0, .5)' },
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
]),
},
data: realData // 使用提取出的 "实际" 数据
},
{
name: '预算',
type: 'line',
@@ -205,6 +181,30 @@ export default {
},
data: targetData // 使用提取出的 "目标" 数据
},
{
name: '实际',
type: 'line',
// stack: 'Total', // 趋势图通常不需要堆叠
symbol: 'circle',
symbolSize: 8,
lineStyle: {
color: 'rgba(255, 132, 0, 1)', // 加深颜色
width: 2,
},
itemStyle: {
color: 'rgba(255, 132, 0, 1)',
borderColor: '#fff',
borderWidth: 2,
},
areaStyle: {
opacity: 0.3,
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(255, 132, 0, .5)' },
{ offset: 1, color: 'rgba(255, 132, 0, 0)' },
]),
},
data: realData // 使用提取出的 "实际" 数据
}
]
};

View File

@@ -91,7 +91,7 @@ export default {
totalData: {},
trend: [],
relatedData: {},
trendName: '原片电费'
trendName: '电费'
};
},

View File

@@ -11,7 +11,17 @@ export default {
return {
myChart: null, // 存储图表实例
resizeHandler: null, // 存储resize事件处理函数
isMounted: false // 图表挂载标志,避免过早执行
isMounted: false, // 图表挂载标志,避免过早执行
baseNameToIndexMap: {
'宜兴': 7,
'漳州': 8,
'自贡': 3,
'桐城': 2,
'洛阳': 9,
'合肥': 5,
'宿迁': 6,
'秦皇岛': 10
}
};
},
props: {

View File

@@ -75,9 +75,9 @@ export default {
indicatorDefs() {
return [
{ key: 'productionCost', name: '制造成本', unit: '元/㎡', route:'/productionCostAnalysis/productionCostAnalysisBase'},
{ key: 'financialCost', name: '财务费用', unit: '万元',route:'/expenseAnalysis/expenseAnalysisBase' },
{ key: 'saleCost', name: '销售费用', unit: '万元',route:'/expenseAnalysis/expenseAnalysisBase'},
{ key: 'manageCost', name: '管理费用', unit: '万元',route:'/expenseAnalysis/expenseAnalysisBase' },
{ key: 'financialCost', name: '财务费用', unit: '万元',route:null },
{ key: 'saleCost', name: '销售费用', unit: '万元',route:null},
{ key: 'manageCost', name: '管理费用', unit: '万元',route:null},
{ key: 'freight', name: '运费', unit: '元/㎡',route:null },
]
},

View File

@@ -89,7 +89,7 @@ export default {
value: 100,
factory: null,
dateData: {},
index: '加工成品率',
index: '投入产出率',
monthData: undefined,
ytdData: undefined,
monthAnalysis: [],

View File

@@ -37,8 +37,8 @@
gap: 12px;
grid-template-columns: 804px 804px;
">
<monthlyRelatedMetrics :factory="factory" :relatedMon="relatedMon" :title="'月度·相关指标分析'" />
<yearRelatedMetrics :factory="factory" :relatedTotal="relatedTotal" :title="'累计·相关指标分析'" />
<monthlyRelatedMetrics :factory="factory" :dateData="dateData" :relatedMon="relatedMon" :title="'月度·相关指标分析'" />
<yearRelatedMetrics :factory="factory" :dateData="dateData" :relatedTotal="relatedTotal" :title="'累计·相关指标分析'" />
</div>
</div>

View File

@@ -179,7 +179,7 @@ export default {
const requestParams = {
startTime: this.dateData.startTime,
endTime: this.dateData.endTime,
trendName: this.trendName,
trendName: '原片'+this.overheadName+'成本',
analysisObject: ['原片'+this.overheadName],
levelId: this.factory,
isOriginal:0,//0:原片 1:加工

View File

@@ -179,12 +179,12 @@ export default {
// 调用接口
getSingleMaterialCostAnalysis(requestParams).then((res) => {
this.monData = res.data.currentMonthData.find(item => {
return item.name === "人工成本";
return item.name === "原片人工成本";
});
console.log('this.monData', this.monData);
this.totalData = res.data.totalMonthData.find(item => {
return item.name === "人工成本";
return item.name === "原片人工成本";
});
this.trend = res.data.dataTrend
});

View File

@@ -60,7 +60,7 @@ import changeBase from "../components/changeBase.vue";
import monthlyOverview from "../productionCostAnalysisComponents/monthlyOverview.vue";
import totalOverview from "../productionCostAnalysisComponents/totalOverview.vue";
import relateSingleFuelCostAnalysis from "../productionCostAnalysisComponents/relateSingleFuelCostAnalysisDian.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendSingleFuelDian.vue";
import dataTrend from "../productionCostAnalysisComponents/dataTrendSingleFuelDian2.vue";
import { mapState } from "vuex";
import { getCostAnalysisData } from '@/api/cockpit'
import moment from "moment";

View File

@@ -275,6 +275,7 @@ export default {
},
handlefuelChange(val) {
this.fuelName = val
if(this.fuelName === '水' && this.trendName === '热耗') return
this.getData()
},
selectChange(data) {

View File

@@ -167,7 +167,7 @@ export default {
startTime: this.dateData.startTime,
endTime: this.dateData.endTime,
trendName: '加工'+this.overheadName+'成本',
analysisObject: ['加工制造费用成本'],
analysisObject: ['加工'+this.overheadName+'成本'],
levelId: this.factory,
isOriginal:1,//0:原片 1:加工
};

View File

@@ -90,7 +90,7 @@ export default {
totalData: {},
trend: [],
relatedData: {},
trendName: '采购单价',
trendName: '加工电成本',
fuelName:'',
fuelOptions: [
{value:'电',label:'电'},
@@ -227,6 +227,7 @@ export default {
},
handleFuelChange(val) {
this.fuelName = val
if(this.fuelName === '水' && this.trendName === '加工电成本') return
this.getData()
},
selectChange(data) {

View File

@@ -60,7 +60,7 @@ export default {
data() {
return {
isDropdownShow: false,
selectedProfit: '', // 选中的名称初始为null
selectedProfit: '加工燃料成本', // 选中的名称初始为null
profitOptions: [
'加工燃料成本',
'电',

View File

@@ -67,11 +67,21 @@ export default {
watch: {
fuelName: {
handler(newVal) {
this.profitOptions.forEach(item => {
if (item.name === this.selectedProfit) {
this.unit = item.unit
}
})
// 检查当前选中的项是否在新的选项列表中
const currentExists = this.profitOptions.some(item => item.name === this.selectedProfit);
if (currentExists) {
// 存在则更新单位
const currentItem = this.profitOptions.find(item => item.name === this.selectedProfit);
this.unit = currentItem.unit;
} else {
// 不存在则重置为第一个选项(采购单价)
const firstOption = this.profitOptions[0];
this.selectedProfit = firstOption.name;
this.unit = firstOption.unit;
// 同时通知父组件更新
this.$emit('handleGetItemData', firstOption.name);
}
}
}
},
@@ -85,6 +95,13 @@ export default {
{ name: '消耗量', unit: '吨'},
{ name: '热耗', unit: '千卡/千克'}
]
}else if(this.fuelName === '水'){
return [
{ name: '采购单价', unit: '元/m³'},
{ name: '产量', unit: '㎡'},
{ name: '单耗', unit: 'm³/㎡'},
{ name: '消耗量', unit: 'm³'}
]
}else{
return [
{ name: '采购单价', unit: '元/m³'},

View File

@@ -60,36 +60,36 @@ export default {
data() {
return {
isDropdownShow: false,
selectedProfit: '', // 初始化为空,由 watch 填充
unit: '' // 初始化为空,由 watch 填充
selectedProfit: '',
unit: ''
};
},
watch: {
// 监听 fuelName 变化,动态设置默认选中项和单位
fuelName: {
handler(newVal) {
if (newVal === '电') {
this.selectedProfit = '原片电成本';
this.unit = '元/㎡';
handler() {
// 检查当前选中的项是否在新的选项列表中
const currentExists = this.profitOptions.some(item => item.name === this.selectedProfit);
if (currentExists) {
// 存在则更新单位
const currentItem = this.profitOptions.find(item => item.name === this.selectedProfit);
this.unit = currentItem.unit;
} else {
this.selectedProfit = '采购单价';
this.unit = '元/m³'; // 注意:原代码非电情况下单位逻辑可能需要确认,这里暂定为 m³ 或根据 profitOptions 动态获取
// 更严谨的做法是根据 profitOptions 查找默认项的单位
const defaultOption = this.profitOptions.find(item => item.name === this.selectedProfit);
if (defaultOption) {
this.unit = defaultOption.unit;
}
// 不存在则重置为第一个选项
const firstOption = this.profitOptions[0];
this.selectedProfit = firstOption.name;
this.unit = firstOption.unit;
// 通知父组件更新数据
this.$emit('handleGetItemData', firstOption.name);
}
},
immediate: true // 组件创建时立即执行一次,解决初始化取值问题
}
}
},
computed: {
profitOptions() {
if (this.fuelName === '电') {
return [
{ name: '原片电成本', unit: '元/㎡' },
// { name: '加工电成本', unit: '元/㎡' },
{ name: '采购单价', unit: '元/度' },
{ name: '产量', unit: '㎡' },
{ name: '单耗', unit: '度/㎡' },
@@ -97,6 +97,7 @@ export default {
]
} else {
return [
// { name: '加工水成本', unit: '元/㎡' },
{ name: '采购单价', unit: '元/m³' },
{ name: '产量', unit: '㎡' },
{ name: '单耗', unit: 'm³/㎡' },

View File

@@ -0,0 +1,500 @@
<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: '原片电成本',
unit: '元/㎡'
};
},
watch: {
fuelName: {
handler() {
// 检查当前选中的项是否在新的选项列表中
const currentExists = this.profitOptions.some(item => item.name === this.selectedProfit);
if (currentExists) {
// 存在则更新单位
const currentItem = this.profitOptions.find(item => item.name === this.selectedProfit);
this.unit = currentItem.unit;
} else {
// 不存在则重置为第一个选项
const firstOption = this.profitOptions[0];
this.selectedProfit = firstOption.name;
this.unit = firstOption.unit;
// 通知父组件更新数据
this.$emit('handleGetItemData', firstOption.name);
}
}
}
},
computed: {
profitOptions() {
return [
{ name: '原片电成本', unit: '元/㎡' },
{ name: '采购单价', unit: '元/度' },
{ name: '产量', unit: '㎡' },
{ name: '单耗', unit: '度/㎡' },
{ name: '消耗量', unit: '度' }
]
},
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: 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. 差值标签(散点图,独立层级)
{
name: '__实际差值标签',
type: 'scatter',
zlevel: 1,
symbolSize: 0,
tooltip: { show: false },
data: (data.value || []).map((value, index) => ({
value: value,
label: {
show: true,
position: 'top',
offset: [0, 0],
width: 68,
height: 20,
formatter: () => {
const currentDiff = (data.diffValue || [])[index] || 0;
const currentFlag = (data.completed || [])[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
}
}
}
}))
}
]
};
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,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 "./dataTrendBarSingleFuelDian2.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

@@ -71,7 +71,7 @@ export default {
{ key: 'naturalGas', name: '天然气', label:'天然气成本',unit: '元/㎡',route:'singleCombustible'},
{ key: 'lng', name: 'LNG液化天然气', label:'LNG液化天然气成本', unit: '元/㎡',route:'singleCombustible'},
{ key: 'heavyOil', name: '重油', label:'重油成本', unit: '元/㎡',route:'singleCombustible'},
{ key: 'water', name: '水', label:'水成本', unit: '元/m³',route:'singleCombustible'}
{ key: 'water', name: '水', label:'原片水成本', unit: '元/m³',route:'singleCombustible'}
]
},
indicators() {

View File

@@ -94,6 +94,13 @@ export default {
{ key: 'haoNum', name: '消耗量', unit: '吨'},
{ key: 'heatConsumption', name: '热耗', unit: '千卡/千克'},
]
}else if(this.fuelName === '水'){
return [
{ key: 'unitPrice', name: '采购单价', unit: '元/m³'},
{ key: 'product', name: '产量', unit: '㎡'},
{ key: 'unitHao', name: '单耗', unit: 'm³/㎡'},
{ key: 'haoNum', name: '消耗量', unit: 'm³'}
]
}else{
return [
{ key: 'unitPrice', name: '采购单价', unit: '元/m³'},

View File

@@ -63,7 +63,8 @@ export default {
real: 0,
target: 0,
thb: 0
}
},
currentTab: 'month'
}
},
computed: {
@@ -123,8 +124,13 @@ export default {
// 监听 relatedData 变化(异步加载场景),同步更新月度数据
relatedData: {
handler(newVal) {
this.relatedDetailData = newVal.relatedMon || {};
if (this.currentTab === 'month') {
this.relatedDetailData = this.relatedData.relatedMon || {};
} else {
this.relatedDetailData = this.relatedData.relatedTotal || {};
}
},
immediate: true,
deep: true
},
@@ -139,17 +145,18 @@ export default {
// 无需额外操作,初始化数据已赋值
},
methods: {
handleRoute(path) {
handleDashboardClick(path) {
this.$router.push({
path: path,
query: {
factory: this.$route.query.factory ? Number(this.$route.query.factory) : 5,
factory: this.$route.query.factory ? this.$route.query.factory : 5,
dateData: this.dateData
}
})
},
handleChange(value) {
console.log('value', value, this.relatedData);
this.currentTab = value;
if (value === 'month') {
this.relatedDetailData = this.relatedData.relatedMon || {};
} else {