改bug
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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>仅允许导入xls、xlsx格式文件。</span>
|
||||
<span>仅允许导入{{timeType==='month'?'月':'年'}}预算!仅允许导入xls、xlsx格式文件!</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();
|
||||
|
||||
@@ -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 // 使用提取出的 "实际" 数据
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ export default {
|
||||
totalData: {},
|
||||
trend: [],
|
||||
relatedData: {},
|
||||
trendName: '原片电费'
|
||||
trendName: '总电费'
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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 },
|
||||
]
|
||||
},
|
||||
|
||||
@@ -89,7 +89,7 @@ export default {
|
||||
value: 100,
|
||||
factory: null,
|
||||
dateData: {},
|
||||
index: '加工成品率',
|
||||
index: '投入产出率',
|
||||
monthData: undefined,
|
||||
ytdData: undefined,
|
||||
monthAnalysis: [],
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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:加工
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -275,6 +275,7 @@ export default {
|
||||
},
|
||||
handlefuelChange(val) {
|
||||
this.fuelName = val
|
||||
if(this.fuelName === '水' && this.trendName === '热耗') return
|
||||
this.getData()
|
||||
},
|
||||
selectChange(data) {
|
||||
|
||||
@@ -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:加工
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -60,7 +60,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
isDropdownShow: false,
|
||||
selectedProfit: '电', // 选中的名称,初始为null
|
||||
selectedProfit: '加工燃料成本', // 选中的名称,初始为null
|
||||
profitOptions: [
|
||||
'加工燃料成本',
|
||||
'电',
|
||||
|
||||
@@ -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³'},
|
||||
|
||||
@@ -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³/㎡' },
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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() {
|
||||
|
||||
@@ -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³'},
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user