l3能源成本分摊
This commit is contained in:
@@ -143,6 +143,15 @@ public class EmsEnergyConsumptionController extends BaseController {
|
|||||||
return R.ok(monthlyConsumption);
|
return R.ok(monthlyConsumption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最近一次抄表的时间范围
|
||||||
|
*/
|
||||||
|
@GetMapping("/latestMeterReadTime")
|
||||||
|
public R<com.klp.ems.domain.vo.LatestMeterReadTimeVo> getLatestMeterReadTime() {
|
||||||
|
com.klp.ems.domain.vo.LatestMeterReadTimeVo meterReadTime = iEmsEnergyConsumptionService.getLatestMeterReadTime();
|
||||||
|
return R.ok(meterReadTime);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增能耗记录
|
* 新增能耗记录
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.klp.ems.controller;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -20,6 +21,8 @@ import com.klp.common.core.validate.EditGroup;
|
|||||||
import com.klp.common.enums.BusinessType;
|
import com.klp.common.enums.BusinessType;
|
||||||
import com.klp.common.utils.poi.ExcelUtil;
|
import com.klp.common.utils.poi.ExcelUtil;
|
||||||
import com.klp.ems.domain.vo.EmsMeterVo;
|
import com.klp.ems.domain.vo.EmsMeterVo;
|
||||||
|
import com.klp.ems.domain.vo.EnergyLinkMatrixVo;
|
||||||
|
import com.klp.ems.domain.vo.EnergyLinkStatisticsVo;
|
||||||
import com.klp.ems.domain.bo.EmsMeterBo;
|
import com.klp.ems.domain.bo.EmsMeterBo;
|
||||||
import com.klp.ems.service.IEmsMeterService;
|
import com.klp.ems.service.IEmsMeterService;
|
||||||
import com.klp.common.core.page.TableDataInfo;
|
import com.klp.common.core.page.TableDataInfo;
|
||||||
@@ -115,4 +118,24 @@ public class EmsMeterController extends BaseController {
|
|||||||
public R<Void> importMeters(@RequestParam("file") MultipartFile file) {
|
public R<Void> importMeters(@RequestParam("file") MultipartFile file) {
|
||||||
return toAjax(iEmsMeterService.importMeters(file));
|
return toAjax(iEmsMeterService.importMeters(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询库区-设备绑定矩阵
|
||||||
|
* 按库区维度查询绑定的设备及其能源类型
|
||||||
|
*/
|
||||||
|
@GetMapping("/matrix")
|
||||||
|
public R<List<EnergyLinkMatrixVo>> queryEnergyLinkMatrix() {
|
||||||
|
List<EnergyLinkMatrixVo> matrix = iEmsMeterService.queryEnergyLinkMatrix();
|
||||||
|
return R.ok(matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询能源绑定统计信息
|
||||||
|
* 统计库区总数、设备总数、绑定总数、能源类型总数
|
||||||
|
*/
|
||||||
|
@GetMapping("/statistics")
|
||||||
|
public R<EnergyLinkStatisticsVo> queryEnergyLinkStatistics() {
|
||||||
|
EnergyLinkStatisticsVo statistics = iEmsMeterService.queryEnergyLinkStatistics();
|
||||||
|
return R.ok(statistics);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,16 @@ import com.klp.common.core.domain.PageQuery;
|
|||||||
import com.klp.common.core.domain.R;
|
import com.klp.common.core.domain.R;
|
||||||
import com.klp.common.core.page.TableDataInfo;
|
import com.klp.common.core.page.TableDataInfo;
|
||||||
import com.klp.common.enums.BusinessType;
|
import com.klp.common.enums.BusinessType;
|
||||||
|
import com.klp.common.utils.poi.ExcelUtil;
|
||||||
import com.klp.ems.domain.bo.EnergyCostReportBo;
|
import com.klp.ems.domain.bo.EnergyCostReportBo;
|
||||||
|
import com.klp.ems.domain.bo.CoilTotalCostBo;
|
||||||
|
import com.klp.ems.domain.bo.WarehouseProductionBo;
|
||||||
|
import com.klp.ems.domain.vo.CoilTotalMergedExportVo;
|
||||||
|
import com.klp.ems.domain.vo.CoilTotalCostDetailVo;
|
||||||
|
import com.klp.ems.domain.vo.CoilTotalCostVo;
|
||||||
import com.klp.ems.domain.vo.EnergyCostSummaryVo;
|
import com.klp.ems.domain.vo.EnergyCostSummaryVo;
|
||||||
|
import com.klp.ems.domain.vo.WarehouseProductionCoilVo;
|
||||||
|
import com.klp.ems.domain.vo.WarehouseProductionStatVo;
|
||||||
import com.klp.ems.domain.vo.WmsEnergyCoilDailyVo;
|
import com.klp.ems.domain.vo.WmsEnergyCoilDailyVo;
|
||||||
import com.klp.ems.service.IEnergyCostReportService;
|
import com.klp.ems.service.IEnergyCostReportService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -16,8 +24,10 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 能源成本报表 Controller
|
* 能源成本报表 Controller
|
||||||
@@ -47,4 +57,56 @@ public class EnergyCostReportController extends BaseController {
|
|||||||
public TableDataInfo<WmsEnergyCoilDailyVo> detail(EnergyCostReportBo bo, PageQuery pageQuery) {
|
public TableDataInfo<WmsEnergyCoilDailyVo> detail(EnergyCostReportBo bo, PageQuery pageQuery) {
|
||||||
return reportService.detail(bo, pageQuery);
|
return reportService.detail(bo, pageQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Log(title = "仓库生产成本", businessType = BusinessType.OTHER)
|
||||||
|
@GetMapping("/warehouseProduction/stats")
|
||||||
|
public R<WarehouseProductionStatVo> warehouseProductionStats(WarehouseProductionBo bo) {
|
||||||
|
return R.ok(reportService.warehouseProductionStats(bo));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Log(title = "仓库生产成本", businessType = BusinessType.OTHER)
|
||||||
|
@GetMapping("/warehouseProduction/detail")
|
||||||
|
public TableDataInfo<WarehouseProductionCoilVo> warehouseProductionDetail(WarehouseProductionBo bo, PageQuery pageQuery) {
|
||||||
|
return reportService.warehouseProductionDetail(bo, pageQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Log(title = "仓库生产成本", businessType = BusinessType.OTHER)
|
||||||
|
@GetMapping("/warehouseProduction/coilDetail")
|
||||||
|
public R<WarehouseProductionCoilVo> warehouseProductionCoilDetail(Long coilId) {
|
||||||
|
return R.ok(reportService.warehouseProductionCoilDetail(coilId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Log(title = "入场卷能源汇总", businessType = BusinessType.OTHER)
|
||||||
|
@GetMapping("/coilTotal/summary")
|
||||||
|
public R<CoilTotalCostVo> coilTotalEnergySummary(CoilTotalCostBo bo) {
|
||||||
|
return R.ok(reportService.coilTotalEnergySummary(bo));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Log(title = "入场卷能源明细", businessType = BusinessType.OTHER)
|
||||||
|
@GetMapping("/coilTotal/detail")
|
||||||
|
public TableDataInfo<CoilTotalCostDetailVo> coilTotalEnergyDetail(CoilTotalCostBo bo, PageQuery pageQuery) {
|
||||||
|
return reportService.coilTotalEnergyDetail(bo, pageQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Log(title = "入场卷成本汇总", businessType = BusinessType.OTHER)
|
||||||
|
@GetMapping("/coilTotal/merged")
|
||||||
|
public TableDataInfo<CoilTotalCostVo> coilTotalMerged(CoilTotalCostBo bo, PageQuery pageQuery) {
|
||||||
|
return reportService.coilTotalMerged(bo, pageQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Log(title = "入场卷成本汇总", businessType = BusinessType.EXPORT)
|
||||||
|
@GetMapping("/coilTotal/merged/export")
|
||||||
|
public void coilTotalMergedExport(CoilTotalCostBo bo, HttpServletResponse response) {
|
||||||
|
List<CoilTotalCostVo> list = reportService.coilTotalMergedExport(bo);
|
||||||
|
List<CoilTotalMergedExportVo> exportList = list.stream().map(item -> {
|
||||||
|
CoilTotalMergedExportVo vo = new CoilTotalMergedExportVo();
|
||||||
|
vo.setEnterCoilNo(item.getEnterCoilNo());
|
||||||
|
vo.setCurrentCoilNo(item.getCurrentCoilNo());
|
||||||
|
vo.setEnergyCost(item.getTotalEnergyCost());
|
||||||
|
vo.setStockCost(item.getStockCost());
|
||||||
|
vo.setTotalCost(item.getTotalCost());
|
||||||
|
return vo;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
ExcelUtil.exportExcel(exportList, "入场卷成本汇总", CoilTotalMergedExportVo.class, response);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.klp.ems.domain.bo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入场钢卷总费用查询参数
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CoilTotalCostBo {
|
||||||
|
/** 入场卷号(模糊) */
|
||||||
|
private String enterCoilNo;
|
||||||
|
/** 当前卷号(模糊) */
|
||||||
|
private String currentCoilNo;
|
||||||
|
/** 最早创建时间过滤(日期) */
|
||||||
|
private String startDate;
|
||||||
|
/** 最早创建时间过滤(日期) */
|
||||||
|
private String endDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.klp.ems.domain.bo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仓库生产统计查询参数
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class WarehouseProductionBo implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 逻辑库区ID */
|
||||||
|
private Long logicWarehouseId;
|
||||||
|
|
||||||
|
/** 开始日期(yyyy-MM-dd) */
|
||||||
|
private String startDate;
|
||||||
|
|
||||||
|
/** 结束日期(yyyy-MM-dd) */
|
||||||
|
private String endDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.klp.ems.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入场钢卷能源费用明细(按流转记录)
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CoilTotalCostDetailVo implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private String enterCoilNo;
|
||||||
|
private Long coilId;
|
||||||
|
private String currentCoilNo;
|
||||||
|
/** 逻辑库区名称 */
|
||||||
|
private String warehouseName;
|
||||||
|
private Long warehouseId;
|
||||||
|
private Integer actionStatus;
|
||||||
|
private Date startTime;
|
||||||
|
private Date endTime;
|
||||||
|
/** 生产时长(小时) */
|
||||||
|
private Double duration;
|
||||||
|
/** 分摊能耗 */
|
||||||
|
private Double consumption;
|
||||||
|
/** 分摊能源成本 */
|
||||||
|
private Double totalCost;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.klp.ems.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 入场钢卷总费用(能源分摊部分)VO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CoilTotalCostVo implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 入场卷号 */
|
||||||
|
private String enterCoilNo;
|
||||||
|
/** 当前卷号(展示用,取同一入场卷号下任一当前卷号) */
|
||||||
|
private String currentCoilNo;
|
||||||
|
/** 关联的钢卷数量 */
|
||||||
|
private Long coilCount;
|
||||||
|
/** 总生产时长(小时) */
|
||||||
|
private Double totalDuration;
|
||||||
|
/** 总分摊能耗 */
|
||||||
|
private Double totalConsumption;
|
||||||
|
/** 总分摊能源成本 */
|
||||||
|
private Double totalEnergyCost;
|
||||||
|
/** 囤积成本(当日/按 stock 计算) */
|
||||||
|
private Double stockCost;
|
||||||
|
/** 净重 */
|
||||||
|
private Double totalNetWeight;
|
||||||
|
/** 毛重 */
|
||||||
|
private Double totalGrossWeight;
|
||||||
|
/** 总成本(能源+囤积) */
|
||||||
|
private Double totalCost;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.klp.ems.domain.vo;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@ExcelIgnoreUnannotated
|
||||||
|
public class CoilTotalMergedExportVo implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ExcelProperty("入场卷号")
|
||||||
|
private String enterCoilNo;
|
||||||
|
|
||||||
|
@ExcelProperty("当前卷号")
|
||||||
|
private String currentCoilNo;
|
||||||
|
|
||||||
|
@ExcelProperty("能源成本")
|
||||||
|
private Double energyCost;
|
||||||
|
|
||||||
|
@ExcelProperty("囤积成本")
|
||||||
|
private Double stockCost;
|
||||||
|
|
||||||
|
@ExcelProperty("总成本")
|
||||||
|
private Double totalCost;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.klp.ems.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class EnergyBreakdownItemVo implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long energyTypeId;
|
||||||
|
private String energyTypeName;
|
||||||
|
private String consumptionUnit;
|
||||||
|
private Double consumption;
|
||||||
|
private Double cost;
|
||||||
|
private Double percentage;
|
||||||
|
private Double averageUnitCost;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.klp.ems.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class WarehouseProductionCoilVo implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private Long coilId;
|
||||||
|
private String coilCode;
|
||||||
|
private String enterCoilNo;
|
||||||
|
private String currentCoilNo;
|
||||||
|
private Long warehouseId;
|
||||||
|
/** 待操作状态 0待上料 1在产 2完成 */
|
||||||
|
private Integer actionStatus;
|
||||||
|
private Date startTime;
|
||||||
|
private Date endTime;
|
||||||
|
/** 生产时长(小时) */
|
||||||
|
private Double duration;
|
||||||
|
/** 分摊能耗 */
|
||||||
|
private Double consumption;
|
||||||
|
/** 分摊成本 */
|
||||||
|
private Double totalCost;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.klp.ems.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class WarehouseProductionStatVo implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 卷数 */
|
||||||
|
private Integer coilCount;
|
||||||
|
/** 总生产时长(小时) */
|
||||||
|
private Double totalDuration;
|
||||||
|
/** 总能耗 */
|
||||||
|
private Double totalConsumption;
|
||||||
|
/** 总成本 */
|
||||||
|
private Double totalCost;
|
||||||
|
/** 单位成本(元/卷) */
|
||||||
|
private Double unitCost;
|
||||||
|
/** 能源分解列表 */
|
||||||
|
private List<EnergyBreakdownItemVo> energyBreakdownList;
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 钢卷能源成本统计 VO
|
* 钢卷能源成本统计 VO
|
||||||
@@ -13,22 +14,27 @@ import java.math.BigDecimal;
|
|||||||
public class WmsEnergyCoilDailyStatisticsVo {
|
public class WmsEnergyCoilDailyStatisticsVo {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 钢卷总数
|
* 生产中总数(status=0,1的钢卷)
|
||||||
*/
|
*/
|
||||||
private Long totalCoils;
|
private Long totalCoils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 总消耗量
|
* 操作总数(status=0,1,2的钢卷)
|
||||||
*/
|
*/
|
||||||
private BigDecimal totalConsumption;
|
private Long totalOperationCoils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 总成本
|
* 平均分摊比率(0-1之间,表示该钢卷占库区总消耗的比例)
|
||||||
|
*/
|
||||||
|
private BigDecimal avgAllocationFactor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总分摊成本
|
||||||
*/
|
*/
|
||||||
private BigDecimal totalCost;
|
private BigDecimal totalCost;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 平均单位成本(¥/小时)
|
* 平均单位成本(¥/单位消耗)
|
||||||
*/
|
*/
|
||||||
private BigDecimal avgUnitCost;
|
private BigDecimal avgUnitCost;
|
||||||
|
|
||||||
@@ -36,4 +42,14 @@ public class WmsEnergyCoilDailyStatisticsVo {
|
|||||||
* 总生产时长(分钟)
|
* 总生产时长(分钟)
|
||||||
*/
|
*/
|
||||||
private Long totalProductionDuration;
|
private Long totalProductionDuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最近一次抄表的开始时间(上一次抄表的结束时间)
|
||||||
|
*/
|
||||||
|
private LocalDateTime latestMeterReadStartTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最近一次抄表的结束时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime latestMeterReadEndTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,4 +81,14 @@ public class WmsEnergyCoilDailyVo {
|
|||||||
|
|
||||||
@ExcelProperty("能源类型名称")
|
@ExcelProperty("能源类型名称")
|
||||||
private String energyTypeName;
|
private String energyTypeName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该库区最近一次抄表的开始时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime latestMeterReadStartTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该库区最近一次抄表的结束时间
|
||||||
|
*/
|
||||||
|
private LocalDateTime latestMeterReadEndTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,4 +57,9 @@ public interface EmsEnergyConsumptionMapper extends BaseMapperPlus<EmsEnergyCons
|
|||||||
* 获取能耗统计信息(用SQL聚合函数计算)
|
* 获取能耗统计信息(用SQL聚合函数计算)
|
||||||
*/
|
*/
|
||||||
Map<String, Object> getStatistics(EmsEnergyConsumptionBo bo);
|
Map<String, Object> getStatistics(EmsEnergyConsumptionBo bo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询最近一次抄表的时间范围
|
||||||
|
*/
|
||||||
|
com.klp.ems.domain.vo.LatestMeterReadTimeVo selectLatestMeterReadTime();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.klp.ems.mapper;
|
|||||||
|
|
||||||
import com.klp.ems.domain.EmsMeter;
|
import com.klp.ems.domain.EmsMeter;
|
||||||
import com.klp.ems.domain.vo.EmsMeterVo;
|
import com.klp.ems.domain.vo.EmsMeterVo;
|
||||||
|
import com.klp.ems.domain.vo.EnergyLinkMatrixVo;
|
||||||
|
import com.klp.ems.domain.vo.EnergyLinkStatisticsVo;
|
||||||
import com.klp.common.core.mapper.BaseMapperPlus;
|
import com.klp.common.core.mapper.BaseMapperPlus;
|
||||||
import com.klp.ems.domain.bo.MeterFilterBo;
|
import com.klp.ems.domain.bo.MeterFilterBo;
|
||||||
|
|
||||||
@@ -19,4 +21,17 @@ public interface EmsMeterMapper extends BaseMapperPlus<EmsMeterMapper, EmsMeter,
|
|||||||
* 根据可选条件筛选计量设备ID列表
|
* 根据可选条件筛选计量设备ID列表
|
||||||
*/
|
*/
|
||||||
java.util.List<Long> selectMeterIds(MeterFilterBo filter);
|
java.util.List<Long> selectMeterIds(MeterFilterBo filter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询库区-设备绑定矩阵
|
||||||
|
* 按库区维度查询绑定的设备及其能源类型
|
||||||
|
*/
|
||||||
|
List<EnergyLinkMatrixVo> selectEnergyLinkMatrix();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询能源绑定统计信息
|
||||||
|
* 统计库区总数、设备总数、绑定总数、能源类型总数
|
||||||
|
*/
|
||||||
|
EnergyLinkStatisticsVo selectEnergyLinkStatistics();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
package com.klp.ems.mapper;
|
package com.klp.ems.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.klp.common.core.mapper.BaseMapperPlus;
|
import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
||||||
|
|
||||||
import com.klp.ems.domain.WmsEnergyCoilDaily;
|
import com.klp.ems.domain.WmsEnergyCoilDaily;
|
||||||
|
import com.klp.ems.domain.bo.CoilTotalCostBo;
|
||||||
import com.klp.ems.domain.bo.EnergyCostReportBo;
|
import com.klp.ems.domain.bo.EnergyCostReportBo;
|
||||||
import com.klp.ems.domain.vo.EnergyCostSummaryVo;
|
import com.klp.ems.domain.bo.WarehouseProductionBo;
|
||||||
import com.klp.ems.domain.vo.WmsEnergyCoilDailyStatisticsVo;
|
import com.klp.ems.domain.bo.WmsEnergyCoilDailyBo;
|
||||||
import com.klp.ems.domain.vo.WmsEnergyCoilDailyVo;
|
import com.klp.ems.domain.vo.*;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -15,11 +19,20 @@ import java.util.Map;
|
|||||||
/**
|
/**
|
||||||
* 钢卷日能源成本分摊结果 Mapper
|
* 钢卷日能源成本分摊结果 Mapper
|
||||||
*/
|
*/
|
||||||
public interface WmsEnergyCoilDailyMapper extends BaseMapperPlus<WmsEnergyCoilDailyMapper, WmsEnergyCoilDaily, WmsEnergyCoilDailyVo> {
|
public interface WmsEnergyCoilDailyMapper extends BaseMapper<WmsEnergyCoilDaily> {
|
||||||
|
|
||||||
/** 物理清理指定任务的明细 */
|
/** 物理清理指定任务的明细 */
|
||||||
int deleteByTaskId(Long taskId);
|
int deleteByTaskId(Long taskId);
|
||||||
|
|
||||||
|
/** 查询单条 */
|
||||||
|
WmsEnergyCoilDailyVo selectVoById(Long energyCostId);
|
||||||
|
|
||||||
|
/** 查询列表 */
|
||||||
|
List<WmsEnergyCoilDailyVo> selectVoList(@Param("ew") Wrapper<WmsEnergyCoilDaily> queryWrapper);
|
||||||
|
|
||||||
|
/** 分页查询 */
|
||||||
|
IPage<WmsEnergyCoilDailyVo> selectVoPage(Page<WmsEnergyCoilDailyVo> page, @Param("ew") Wrapper<WmsEnergyCoilDaily> queryWrapper);
|
||||||
|
|
||||||
/** 查询指定任务的所有分摊记录 */
|
/** 查询指定任务的所有分摊记录 */
|
||||||
List<WmsEnergyCoilDaily> selectListByTask(Long taskId);
|
List<WmsEnergyCoilDaily> selectListByTask(Long taskId);
|
||||||
|
|
||||||
@@ -39,7 +52,37 @@ public interface WmsEnergyCoilDailyMapper extends BaseMapperPlus<WmsEnergyCoilDa
|
|||||||
Map<String, Object> selectEnergyOverview(@Param("bo") EnergyCostReportBo bo);
|
Map<String, Object> selectEnergyOverview(@Param("bo") EnergyCostReportBo bo);
|
||||||
|
|
||||||
/** 明细分页 */
|
/** 明细分页 */
|
||||||
Page<WmsEnergyCoilDailyVo> selectReportDetail(Page<WmsEnergyCoilDailyVo> page, @Param("bo") EnergyCostReportBo bo);
|
IPage<WmsEnergyCoilDailyVo> selectReportDetail(Page<WmsEnergyCoilDailyVo> page, @Param("bo") EnergyCostReportBo bo);
|
||||||
|
|
||||||
|
/** 仓库生产统计 */
|
||||||
|
WarehouseProductionStatVo selectWarehouseProductionStats(@Param("bo") WarehouseProductionBo bo);
|
||||||
|
|
||||||
|
/** 仓库生产明细 */
|
||||||
|
IPage<WarehouseProductionCoilVo> selectWarehouseProductionDetail(Page<WarehouseProductionCoilVo> page, @Param("bo") WarehouseProductionBo bo);
|
||||||
|
|
||||||
|
/** 仓库生产能源分解 */
|
||||||
|
List<EnergyBreakdownItemVo> selectWarehouseProductionEnergyBreakdown(@Param("bo") WarehouseProductionBo bo);
|
||||||
|
|
||||||
|
/** 单卷详情(含分摊成本) */
|
||||||
|
WarehouseProductionCoilVo selectWarehouseProductionCoilDetail(@Param("coilId") Long coilId);
|
||||||
|
|
||||||
|
/** 入场卷号维度能源费用汇总 */
|
||||||
|
CoilTotalCostVo selectCoilTotalEnergySummary(@Param("bo") CoilTotalCostBo bo);
|
||||||
|
|
||||||
|
/** 入场卷号维度能源费用明细 */
|
||||||
|
IPage<CoilTotalCostDetailVo> selectCoilTotalEnergyDetail(Page<CoilTotalCostDetailVo> page, @Param("bo") CoilTotalCostBo bo);
|
||||||
|
|
||||||
|
/** 入场卷号维度能源+囤积成本汇总 */
|
||||||
|
@com.baomidou.mybatisplus.annotation.InterceptorIgnore(tenantLine = "true", blockAttack = "true", dynamicTableName = "true", illegalSql = "true")
|
||||||
|
List<CoilTotalCostVo> selectCoilTotalMerged(@Param("bo") CoilTotalCostBo bo,
|
||||||
|
@Param("offset") long offset,
|
||||||
|
@Param("pageSize") long pageSize);
|
||||||
|
|
||||||
|
/** 入场卷号维度能源+囤积成本汇总总数 */
|
||||||
|
Long selectCoilTotalMergedCount(@Param("bo") CoilTotalCostBo bo);
|
||||||
|
|
||||||
|
/** 入场卷号维度能源+囤积成本汇总(导出) */
|
||||||
|
List<CoilTotalCostVo> selectCoilTotalMergedExport(@Param("bo") CoilTotalCostBo bo);
|
||||||
|
|
||||||
/** 查询待操作钢卷的能源成本(一次性SQL查询) */
|
/** 查询待操作钢卷的能源成本(一次性SQL查询) */
|
||||||
List<WmsEnergyCoilDailyVo> selectPendingActionCoilCost();
|
List<WmsEnergyCoilDailyVo> selectPendingActionCoilCost();
|
||||||
|
|||||||
@@ -95,4 +95,10 @@ public interface IEmsEnergyConsumptionService {
|
|||||||
* @return 统计信息(总记录数、总消耗量、平均消耗量、最大消耗量等)
|
* @return 统计信息(总记录数、总消耗量、平均消耗量、最大消耗量等)
|
||||||
*/
|
*/
|
||||||
Map<String, Object> getStatistics(EmsEnergyConsumptionBo bo);
|
Map<String, Object> getStatistics(EmsEnergyConsumptionBo bo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最近一次抄表的时间范围
|
||||||
|
* @return 最近一次抄表的开始时间和结束时间
|
||||||
|
*/
|
||||||
|
com.klp.ems.domain.vo.LatestMeterReadTimeVo getLatestMeterReadTime();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package com.klp.ems.service;
|
|||||||
|
|
||||||
import com.klp.ems.domain.EmsMeter;
|
import com.klp.ems.domain.EmsMeter;
|
||||||
import com.klp.ems.domain.vo.EmsMeterVo;
|
import com.klp.ems.domain.vo.EmsMeterVo;
|
||||||
|
import com.klp.ems.domain.vo.EnergyLinkMatrixVo;
|
||||||
|
import com.klp.ems.domain.vo.EnergyLinkStatisticsVo;
|
||||||
import com.klp.ems.domain.bo.EmsMeterBo;
|
import com.klp.ems.domain.bo.EmsMeterBo;
|
||||||
import com.klp.common.core.page.TableDataInfo;
|
import com.klp.common.core.page.TableDataInfo;
|
||||||
import com.klp.common.core.domain.PageQuery;
|
import com.klp.common.core.domain.PageQuery;
|
||||||
@@ -58,4 +60,16 @@ public interface IEmsMeterService {
|
|||||||
* 导入设备
|
* 导入设备
|
||||||
*/
|
*/
|
||||||
Boolean importMeters(MultipartFile file);
|
Boolean importMeters(MultipartFile file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询库区-设备绑定矩阵
|
||||||
|
* 按库区维度查询绑定的设备及其能源类型
|
||||||
|
*/
|
||||||
|
List<EnergyLinkMatrixVo> queryEnergyLinkMatrix();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询能源绑定统计信息
|
||||||
|
* 统计库区总数、设备总数、绑定总数、能源类型总数
|
||||||
|
*/
|
||||||
|
EnergyLinkStatisticsVo queryEnergyLinkStatistics();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,13 @@ package com.klp.ems.service;
|
|||||||
import com.klp.common.core.domain.PageQuery;
|
import com.klp.common.core.domain.PageQuery;
|
||||||
import com.klp.common.core.page.TableDataInfo;
|
import com.klp.common.core.page.TableDataInfo;
|
||||||
import com.klp.ems.domain.bo.EnergyCostReportBo;
|
import com.klp.ems.domain.bo.EnergyCostReportBo;
|
||||||
|
import com.klp.ems.domain.bo.CoilTotalCostBo;
|
||||||
|
import com.klp.ems.domain.bo.WarehouseProductionBo;
|
||||||
|
import com.klp.ems.domain.vo.CoilTotalCostDetailVo;
|
||||||
|
import com.klp.ems.domain.vo.CoilTotalCostVo;
|
||||||
import com.klp.ems.domain.vo.EnergyCostSummaryVo;
|
import com.klp.ems.domain.vo.EnergyCostSummaryVo;
|
||||||
|
import com.klp.ems.domain.vo.WarehouseProductionCoilVo;
|
||||||
|
import com.klp.ems.domain.vo.WarehouseProductionStatVo;
|
||||||
import com.klp.ems.domain.vo.WmsEnergyCoilDailyVo;
|
import com.klp.ems.domain.vo.WmsEnergyCoilDailyVo;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -22,4 +28,25 @@ public interface IEnergyCostReportService {
|
|||||||
|
|
||||||
/** 明细分页 */
|
/** 明细分页 */
|
||||||
TableDataInfo<WmsEnergyCoilDailyVo> detail(EnergyCostReportBo bo, PageQuery pageQuery);
|
TableDataInfo<WmsEnergyCoilDailyVo> detail(EnergyCostReportBo bo, PageQuery pageQuery);
|
||||||
|
|
||||||
|
/** 仓库生产统计 */
|
||||||
|
WarehouseProductionStatVo warehouseProductionStats(WarehouseProductionBo bo);
|
||||||
|
|
||||||
|
/** 仓库生产明细 */
|
||||||
|
TableDataInfo<WarehouseProductionCoilVo> warehouseProductionDetail(WarehouseProductionBo bo, PageQuery pageQuery);
|
||||||
|
|
||||||
|
/** 单卷详情 */
|
||||||
|
WarehouseProductionCoilVo warehouseProductionCoilDetail(Long coilId);
|
||||||
|
|
||||||
|
/** 入场卷号维度能源费用汇总 */
|
||||||
|
CoilTotalCostVo coilTotalEnergySummary(CoilTotalCostBo bo);
|
||||||
|
|
||||||
|
/** 入场卷号维度能源费用明细 */
|
||||||
|
TableDataInfo<CoilTotalCostDetailVo> coilTotalEnergyDetail(CoilTotalCostBo bo, PageQuery pageQuery);
|
||||||
|
|
||||||
|
/** 入场卷号维度能源+囤积综合汇总 */
|
||||||
|
TableDataInfo<CoilTotalCostVo> coilTotalMerged(CoilTotalCostBo bo, PageQuery pageQuery);
|
||||||
|
|
||||||
|
/** 入场卷号维度能源+囤积综合汇总导出 */
|
||||||
|
java.util.List<CoilTotalCostVo> coilTotalMergedExport(CoilTotalCostBo bo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -558,4 +558,12 @@ public class EmsEnergyConsumptionServiceImpl implements IEmsEnergyConsumptionSer
|
|||||||
}
|
}
|
||||||
return stats;
|
return stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取最近一次抄表的时间范围
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public com.klp.ems.domain.vo.LatestMeterReadTimeVo getLatestMeterReadTime() {
|
||||||
|
return baseMapper.selectLatestMeterReadTime();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import com.klp.ems.domain.bo.EmsMeterBo;
|
import com.klp.ems.domain.bo.EmsMeterBo;
|
||||||
import com.klp.ems.domain.vo.EmsMeterVo;
|
import com.klp.ems.domain.vo.EmsMeterVo;
|
||||||
|
import com.klp.ems.domain.vo.EnergyLinkMatrixVo;
|
||||||
|
import com.klp.ems.domain.vo.EnergyLinkStatisticsVo;
|
||||||
import com.klp.ems.domain.EmsMeter;
|
import com.klp.ems.domain.EmsMeter;
|
||||||
import com.klp.ems.domain.EmsEnergyType;
|
import com.klp.ems.domain.EmsEnergyType;
|
||||||
import com.klp.ems.domain.dto.MeterTemplateDto;
|
import com.klp.ems.domain.dto.MeterTemplateDto;
|
||||||
@@ -23,8 +25,8 @@ import com.klp.ems.service.IEmsMeterService;
|
|||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计量设备(阈值移至此处)Service业务层处理
|
* 计量设备(阈值移至此处)Service业务层处理
|
||||||
@@ -231,4 +233,22 @@ public class EmsMeterServiceImpl implements IEmsMeterService {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询库区-设备绑定矩阵
|
||||||
|
* 按库区维度查询绑定的设备及其能源类型
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<EnergyLinkMatrixVo> queryEnergyLinkMatrix() {
|
||||||
|
return baseMapper.selectEnergyLinkMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询能源绑定统计信息
|
||||||
|
* 统计库区总数、设备总数、绑定总数、能源类型总数
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public EnergyLinkStatisticsVo queryEnergyLinkStatistics() {
|
||||||
|
return baseMapper.selectEnergyLinkStatistics();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
package com.klp.ems.service.impl;
|
package com.klp.ems.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.klp.common.core.domain.PageQuery;
|
import com.klp.common.core.domain.PageQuery;
|
||||||
import com.klp.common.core.page.TableDataInfo;
|
import com.klp.common.core.page.TableDataInfo;
|
||||||
|
import com.klp.ems.domain.bo.CoilTotalCostBo;
|
||||||
import com.klp.ems.domain.bo.EnergyCostReportBo;
|
import com.klp.ems.domain.bo.EnergyCostReportBo;
|
||||||
|
import com.klp.ems.domain.bo.WarehouseProductionBo;
|
||||||
|
import com.klp.ems.domain.vo.CoilTotalCostDetailVo;
|
||||||
|
import com.klp.ems.domain.vo.CoilTotalCostVo;
|
||||||
import com.klp.ems.domain.vo.EnergyCostSummaryVo;
|
import com.klp.ems.domain.vo.EnergyCostSummaryVo;
|
||||||
|
import com.klp.ems.domain.vo.WarehouseProductionCoilVo;
|
||||||
|
import com.klp.ems.domain.vo.WarehouseProductionStatVo;
|
||||||
import com.klp.ems.domain.vo.WmsEnergyCoilDailyVo;
|
import com.klp.ems.domain.vo.WmsEnergyCoilDailyVo;
|
||||||
import com.klp.ems.mapper.WmsEnergyCoilDailyMapper;
|
import com.klp.ems.mapper.WmsEnergyCoilDailyMapper;
|
||||||
import com.klp.ems.service.IEnergyCostReportService;
|
import com.klp.ems.service.IEnergyCostReportService;
|
||||||
@@ -49,7 +56,90 @@ public class EnergyCostReportServiceImpl implements IEnergyCostReportService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TableDataInfo<WmsEnergyCoilDailyVo> detail(EnergyCostReportBo bo, PageQuery pageQuery) {
|
public TableDataInfo<WmsEnergyCoilDailyVo> detail(EnergyCostReportBo bo, PageQuery pageQuery) {
|
||||||
Page<WmsEnergyCoilDailyVo> page = coilDailyMapper.selectReportDetail(pageQuery.build(), bo);
|
IPage<WmsEnergyCoilDailyVo> page = coilDailyMapper.selectReportDetail(pageQuery.build(), bo);
|
||||||
return TableDataInfo.build(page);
|
return TableDataInfo.build(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WarehouseProductionStatVo warehouseProductionStats(WarehouseProductionBo bo) {
|
||||||
|
WarehouseProductionStatVo statVo = coilDailyMapper.selectWarehouseProductionStats(bo);
|
||||||
|
if (statVo == null) {
|
||||||
|
statVo = new WarehouseProductionStatVo();
|
||||||
|
}
|
||||||
|
// 能源分解
|
||||||
|
List<com.klp.ems.domain.vo.EnergyBreakdownItemVo> items = coilDailyMapper.selectWarehouseProductionEnergyBreakdown(bo);
|
||||||
|
if (items == null) {
|
||||||
|
items = Collections.emptyList();
|
||||||
|
}
|
||||||
|
double totalCost = items.stream().mapToDouble(i -> i.getCost() == null ? 0D : i.getCost()).sum();
|
||||||
|
for (com.klp.ems.domain.vo.EnergyBreakdownItemVo item : items) {
|
||||||
|
double cost = item.getCost() == null ? 0D : item.getCost();
|
||||||
|
double consumption = item.getConsumption() == null ? 0D : item.getConsumption();
|
||||||
|
item.setPercentage(totalCost > 0 ? (cost * 100 / totalCost) : 0D);
|
||||||
|
item.setAverageUnitCost(consumption > 0 ? (cost / consumption) : 0D);
|
||||||
|
}
|
||||||
|
statVo.setEnergyBreakdownList(items);
|
||||||
|
// unitCost 补充
|
||||||
|
if (statVo.getUnitCost() == null || statVo.getUnitCost() == 0) {
|
||||||
|
double coils = statVo.getCoilCount() == null ? 0D : statVo.getCoilCount();
|
||||||
|
double cost = statVo.getTotalCost() == null ? 0D : statVo.getTotalCost();
|
||||||
|
statVo.setUnitCost(coils > 0 ? cost / coils : 0D);
|
||||||
|
}
|
||||||
|
return statVo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableDataInfo<WarehouseProductionCoilVo> warehouseProductionDetail(WarehouseProductionBo bo, PageQuery pageQuery) {
|
||||||
|
if (pageQuery.getPageSize() == null) {
|
||||||
|
pageQuery.setPageSize(15);
|
||||||
|
}
|
||||||
|
IPage<WarehouseProductionCoilVo> page = coilDailyMapper.selectWarehouseProductionDetail(pageQuery.build(), bo);
|
||||||
|
return TableDataInfo.build(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WarehouseProductionCoilVo warehouseProductionCoilDetail(Long coilId) {
|
||||||
|
return coilDailyMapper.selectWarehouseProductionCoilDetail(coilId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CoilTotalCostVo coilTotalEnergySummary(CoilTotalCostBo bo) {
|
||||||
|
CoilTotalCostVo vo = coilDailyMapper.selectCoilTotalEnergySummary(bo);
|
||||||
|
if (vo == null) {
|
||||||
|
vo = new CoilTotalCostVo();
|
||||||
|
vo.setTotalEnergyCost(0D);
|
||||||
|
vo.setTotalConsumption(0D);
|
||||||
|
vo.setTotalDuration(0D);
|
||||||
|
vo.setCoilCount(0L);
|
||||||
|
}
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableDataInfo<CoilTotalCostDetailVo> coilTotalEnergyDetail(CoilTotalCostBo bo, PageQuery pageQuery) {
|
||||||
|
if (pageQuery.getPageSize() == null) {
|
||||||
|
pageQuery.setPageSize(15);
|
||||||
|
}
|
||||||
|
IPage<CoilTotalCostDetailVo> page = coilDailyMapper.selectCoilTotalEnergyDetail(pageQuery.build(), bo);
|
||||||
|
return TableDataInfo.build(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableDataInfo<CoilTotalCostVo> coilTotalMerged(CoilTotalCostBo bo, PageQuery pageQuery) {
|
||||||
|
long pageNum = pageQuery.getPageNum() == null ? 1L : pageQuery.getPageNum();
|
||||||
|
long pageSize = pageQuery.getPageSize() == null ? 15L : pageQuery.getPageSize();
|
||||||
|
long offset = (pageNum - 1) * pageSize;
|
||||||
|
// 手工 count 与数据分页,避免复杂 SQL 自动 count 解析
|
||||||
|
Long total = coilDailyMapper.selectCoilTotalMergedCount(bo);
|
||||||
|
java.util.List<CoilTotalCostVo> rows = coilDailyMapper.selectCoilTotalMerged(bo, offset, pageSize);
|
||||||
|
Page<CoilTotalCostVo> page = new Page<>(pageNum, pageSize);
|
||||||
|
page.setTotal(total == null ? 0L : total);
|
||||||
|
page.setRecords(rows);
|
||||||
|
return TableDataInfo.build(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public java.util.List<CoilTotalCostVo> coilTotalMergedExport(CoilTotalCostBo bo) {
|
||||||
|
return coilDailyMapper.selectCoilTotalMergedExport(bo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.klp.ems.service.impl;
|
|||||||
import cn.hutool.core.bean.BeanUtil;
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.klp.common.core.domain.PageQuery;
|
import com.klp.common.core.domain.PageQuery;
|
||||||
@@ -37,7 +38,7 @@ public class WmsEnergyCoilDailyServiceImpl implements IWmsEnergyCoilDailyService
|
|||||||
@Override
|
@Override
|
||||||
public TableDataInfo<WmsEnergyCoilDailyVo> queryPageList(WmsEnergyCoilDailyBo bo, PageQuery pageQuery) {
|
public TableDataInfo<WmsEnergyCoilDailyVo> queryPageList(WmsEnergyCoilDailyBo bo, PageQuery pageQuery) {
|
||||||
LambdaQueryWrapper<WmsEnergyCoilDaily> lqw = buildQueryWrapper(bo);
|
LambdaQueryWrapper<WmsEnergyCoilDaily> lqw = buildQueryWrapper(bo);
|
||||||
Page<WmsEnergyCoilDailyVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
IPage<WmsEnergyCoilDailyVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||||
return TableDataInfo.build(result);
|
return TableDataInfo.build(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -163,4 +163,15 @@
|
|||||||
</if>
|
</if>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询最近一次抄表的时间范围 -->
|
||||||
|
<select id="selectLatestMeterReadTime" resultType="com.klp.ems.domain.vo.LatestMeterReadTimeVo">
|
||||||
|
SELECT
|
||||||
|
start_time AS startTime,
|
||||||
|
end_time AS endTime
|
||||||
|
FROM ems_energy_consumption
|
||||||
|
WHERE end_time IS NOT NULL
|
||||||
|
ORDER BY end_time DESC
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -39,6 +39,30 @@
|
|||||||
<result property="warehouseName" column="warehouse_name"/>
|
<result property="warehouseName" column="warehouse_name"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
|
<resultMap type="com.klp.ems.domain.vo.EnergyLinkMatrixVo" id="EnergyLinkMatrixVoResult">
|
||||||
|
<result property="warehouseId" column="warehouse_id"/>
|
||||||
|
<result property="warehouseName" column="warehouse_name"/>
|
||||||
|
<result property="warehouseCode" column="warehouse_code"/>
|
||||||
|
<collection property="links" ofType="com.klp.ems.domain.vo.EnergyLinkDetailVo" resultMap="EnergyLinkDetailVoResult"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<resultMap type="com.klp.ems.domain.vo.EnergyLinkDetailVo" id="EnergyLinkDetailVoResult">
|
||||||
|
<result property="linkId" column="link_id"/>
|
||||||
|
<result property="meterId" column="meter_id"/>
|
||||||
|
<result property="meterCode" column="meter_code"/>
|
||||||
|
<result property="energyTypeId" column="energy_type_id"/>
|
||||||
|
<result property="energyTypeName" column="energy_type_name"/>
|
||||||
|
<result property="model" column="model"/>
|
||||||
|
<result property="manufacturer" column="manufacturer"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<resultMap type="com.klp.ems.domain.vo.EnergyLinkStatisticsVo" id="EnergyLinkStatisticsVoResult">
|
||||||
|
<result property="totalWarehouses" column="totalWarehouses"/>
|
||||||
|
<result property="totalMeters" column="totalMeters"/>
|
||||||
|
<result property="totalLinks" column="totalLinks"/>
|
||||||
|
<result property="totalEnergyTypes" column="totalEnergyTypes"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
<!-- 查询设备列表,包含库区信息 -->
|
<!-- 查询设备列表,包含库区信息 -->
|
||||||
<select id="selectVoList" resultMap="EmsMeterVoResult">
|
<select id="selectVoList" resultMap="EmsMeterVoResult">
|
||||||
SELECT
|
SELECT
|
||||||
@@ -90,4 +114,34 @@
|
|||||||
</foreach>
|
</foreach>
|
||||||
</if>
|
</if>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询库区-设备绑定矩阵 -->
|
||||||
|
<select id="selectEnergyLinkMatrix" resultMap="EnergyLinkMatrixVoResult">
|
||||||
|
SELECT
|
||||||
|
w.warehouse_id,
|
||||||
|
w.warehouse_name,
|
||||||
|
w.warehouse_code,
|
||||||
|
l.link_id,
|
||||||
|
l.meter_id,
|
||||||
|
m.meter_code,
|
||||||
|
m.energy_type_id,
|
||||||
|
et.name AS energy_type_name,
|
||||||
|
m.model,
|
||||||
|
m.manufacturer
|
||||||
|
FROM wms_warehouse w
|
||||||
|
LEFT JOIN wms_energy_area_link l ON w.warehouse_id = l.warehouse_id AND l.is_enabled = 1
|
||||||
|
LEFT JOIN ems_meter m ON l.meter_id = m.meter_id
|
||||||
|
LEFT JOIN ems_energy_type et ON m.energy_type_id = et.energy_type_id
|
||||||
|
WHERE w.del_flag = '0'
|
||||||
|
ORDER BY w.warehouse_id, l.link_id
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 查询能源绑定统计信息 -->
|
||||||
|
<select id="selectEnergyLinkStatistics" resultMap="EnergyLinkStatisticsVoResult">
|
||||||
|
SELECT
|
||||||
|
(SELECT COUNT(DISTINCT warehouse_id) FROM wms_warehouse WHERE del_flag = '0') AS totalWarehouses,
|
||||||
|
(SELECT COUNT(DISTINCT meter_id) FROM ems_meter WHERE del_flag = '0') AS totalMeters,
|
||||||
|
(SELECT COUNT(DISTINCT link_id) FROM wms_energy_area_link WHERE is_enabled = 1) AS totalLinks,
|
||||||
|
(SELECT COUNT(DISTINCT energy_type_id) FROM ems_energy_type WHERE del_flag = '0') AS totalEnergyTypes
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -122,6 +122,24 @@ export function listPendingActionCoilCost(query) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询待操作钢卷成本详情(单卷)
|
||||||
|
export function getPendingActionCoilCostDetail(coilId) {
|
||||||
|
return request({
|
||||||
|
url: '/ems/energy/coilDaily/pendingAction/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: { coilId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询待操作钢卷的能源成本统计(只统计status=0,1的钢卷)
|
||||||
|
export function getPendingActionCoilCostStatistics(query) {
|
||||||
|
return request({
|
||||||
|
url: '/ems/energy/coilDaily/pendingAction/statistics',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 导出钢卷能源成本分摊
|
// 导出钢卷能源成本分摊
|
||||||
export function exportEnergyCoilDaily(query) {
|
export function exportEnergyCoilDaily(query) {
|
||||||
return request({
|
return request({
|
||||||
|
|||||||
@@ -72,3 +72,11 @@ export function getStatistics(query) {
|
|||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取最近一次抄表的时间范围
|
||||||
|
export function getLatestMeterReadTime() {
|
||||||
|
return request({
|
||||||
|
url: '/ems/energyConsumption/latestMeterReadTime',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,3 +23,40 @@ export function fetchEnergyDetail(query) {
|
|||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 入场卷能源汇总
|
||||||
|
export function fetchCoilTotalEnergySummary(query) {
|
||||||
|
return request({
|
||||||
|
url: '/ems/energy/report/coilTotal/summary',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 入场卷能源明细
|
||||||
|
export function fetchCoilTotalEnergyDetail(query) {
|
||||||
|
return request({
|
||||||
|
url: '/ems/energy/report/coilTotal/detail',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 入场卷能源+囤积汇总
|
||||||
|
export function fetchCoilTotalMerged(query) {
|
||||||
|
return request({
|
||||||
|
url: '/ems/energy/report/coilTotal/merged',
|
||||||
|
method: 'get',
|
||||||
|
params: query
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 入场卷能源+囤积汇总导出
|
||||||
|
export function exportCoilTotalMerged(query) {
|
||||||
|
return request({
|
||||||
|
url: '/ems/energy/report/coilTotal/merged/export',
|
||||||
|
method: 'get',
|
||||||
|
params: query,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export function delEnergyLink(linkId) {
|
|||||||
|
|
||||||
export function fetchEnergyLinkMatrix(query) {
|
export function fetchEnergyLinkMatrix(query) {
|
||||||
return request({
|
return request({
|
||||||
url: '/ems/energy/link/matrix',
|
url: '/mes/meter/matrix',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
@@ -65,7 +65,7 @@ export function exportEnergyLinks(query) {
|
|||||||
|
|
||||||
export function getStatistics() {
|
export function getStatistics() {
|
||||||
return request({
|
return request({
|
||||||
url: '/ems/energy/link/statistics',
|
url: '/mes/meter/statistics',
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default {
|
|||||||
pageSizes: {
|
pageSizes: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default() {
|
default() {
|
||||||
return [10, 20, 30, 50]
|
return [15, 20, 30, 50]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 移动端页码按钮的数量端默认值5
|
// 移动端页码按钮的数量端默认值5
|
||||||
|
|||||||
@@ -3,21 +3,17 @@
|
|||||||
<!-- 统计信息 -->
|
<!-- 统计信息 -->
|
||||||
<div class="statistics-section">
|
<div class="statistics-section">
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<div class="stat-label">钢卷总数</div>
|
<div class="stat-label">生产中总数</div>
|
||||||
<div class="stat-value">{{ statistics.totalCoils }}</div>
|
<div class="stat-value">{{ statistics.totalCoils }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<div class="stat-label">总消耗量</div>
|
<div class="stat-label">生产总数</div>
|
||||||
<div class="stat-value">{{ formatNumber(statistics.totalConsumption, 2) }}</div>
|
<div class="stat-value">{{ statistics.totalOperationCoils }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<div class="stat-label">总成本</div>
|
<div class="stat-label">预估总成本</div>
|
||||||
<div class="stat-value cost-highlight">¥ {{ formatNumber(statistics.totalCost, 2) }}</div>
|
<div class="stat-value cost-highlight">¥ {{ formatNumber(statistics.totalCost, 2) }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-item">
|
|
||||||
<div class="stat-label">平均单位成本</div>
|
|
||||||
<div class="stat-value">¥ {{ formatNumber(statistics.avgUnitCost, 2) }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 检索条件 -->
|
<!-- 检索条件 -->
|
||||||
@@ -65,6 +61,19 @@
|
|||||||
<el-table-column prop="currentCoilNo" label="当前卷号" min-width="120"></el-table-column>
|
<el-table-column prop="currentCoilNo" label="当前卷号" min-width="120"></el-table-column>
|
||||||
<el-table-column prop="enterCoilNo" label="入场卷号" min-width="120"></el-table-column>
|
<el-table-column prop="enterCoilNo" label="入场卷号" min-width="120"></el-table-column>
|
||||||
<el-table-column prop="warehouseName" label="逻辑库区" min-width="120"></el-table-column>
|
<el-table-column prop="warehouseName" label="逻辑库区" min-width="120"></el-table-column>
|
||||||
|
<el-table-column label="分摊时间范围" min-width="200">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span v-if="scope.row.latestMeterReadStartTime && scope.row.latestMeterReadEndTime">
|
||||||
|
{{ formatDateOnly(scope.row.latestMeterReadStartTime) }} ~ {{ formatDateOnly(scope.row.latestMeterReadEndTime) }}
|
||||||
|
</span>
|
||||||
|
<span v-else-if="!scope.row.latestMeterReadStartTime && !scope.row.latestMeterReadEndTime" class="no-data-tip">
|
||||||
|
当前库区未绑定任何设备
|
||||||
|
</span>
|
||||||
|
<span v-else class="no-data-tip">
|
||||||
|
当前未完成任何数据录入
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="生产时间" min-width="200">
|
<el-table-column label="生产时间" min-width="200">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{ formatTime(null, null, scope.row.scanTime) }} - {{ formatTime(null, null, scope.row.completeTime) }}
|
{{ formatTime(null, null, scope.row.scanTime) }} - {{ formatTime(null, null, scope.row.completeTime) }}
|
||||||
@@ -75,9 +84,9 @@
|
|||||||
{{ formatNumber(scope.row.productionDuration / 60, 2) }}
|
{{ formatNumber(scope.row.productionDuration / 60, 2) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="consumptionQty" label="综合消耗(单位)" min-width="120" align="right">
|
<el-table-column prop="allocationFactor" label="分摊比率(%)" min-width="120" align="right">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{ formatNumber(scope.row.consumptionQty, 2) }}
|
{{ formatNumber(scope.row.allocationFactor * 100, 2) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="costAmount" label="综合成本(¥)" min-width="120" align="right" sortable>
|
<el-table-column prop="costAmount" label="综合成本(¥)" min-width="120" align="right" sortable>
|
||||||
@@ -85,11 +94,6 @@
|
|||||||
<span class="cost-value">{{ formatNumber(scope.row.costAmount, 2) }}</span>
|
<span class="cost-value">{{ formatNumber(scope.row.costAmount, 2) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="单位成本(¥/h)" min-width="120" align="right">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
{{ formatNumber(scope.row.costAmount / (scope.row.productionDuration / 60), 2) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="100" fixed="right">
|
<el-table-column label="操作" width="100" fixed="right">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button type="primary" size="small" @click="showDetail(scope.row)">详情</el-button>
|
<el-button type="primary" size="small" @click="showDetail(scope.row)">详情</el-button>
|
||||||
@@ -220,7 +224,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { listEnergyCoilDaily, listPendingActionCoilCost } from '@/api/ems/energyAllocation'
|
import { listEnergyCoilDaily, listPendingActionCoilCost, getPendingActionCoilCostStatistics } from '@/api/ems/energyAllocation'
|
||||||
|
import { getLatestMeterReadTime } from '@/api/ems/energyConsumption'
|
||||||
|
import { getPendingActionCoilCostDetail } from '@/api/ems/energyAllocation'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CoilCost',
|
name: 'CoilCost',
|
||||||
@@ -234,7 +240,7 @@ export default {
|
|||||||
warehouseList: [],
|
warehouseList: [],
|
||||||
statistics: {
|
statistics: {
|
||||||
totalCoils: 0,
|
totalCoils: 0,
|
||||||
totalConsumption: 0,
|
avgAllocationFactor: 0,
|
||||||
totalCost: 0,
|
totalCost: 0,
|
||||||
avgUnitCost: 0
|
avgUnitCost: 0
|
||||||
},
|
},
|
||||||
@@ -252,6 +258,7 @@ export default {
|
|||||||
mounted() {
|
mounted() {
|
||||||
this.loadWarehouseList();
|
this.loadWarehouseList();
|
||||||
this.loadCoilCostList();
|
this.loadCoilCostList();
|
||||||
|
this.loadLatestMeterReadTime();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loadWarehouseList() {
|
loadWarehouseList() {
|
||||||
@@ -261,15 +268,38 @@ export default {
|
|||||||
},
|
},
|
||||||
loadCoilCostList() {
|
loadCoilCostList() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
// 优先加载待操作钢卷的成本
|
// 同时加载统计数据和列表数据
|
||||||
|
this.fetchStatistics();
|
||||||
this.fetchPendingActionCoilCost();
|
this.fetchPendingActionCoilCost();
|
||||||
},
|
},
|
||||||
|
fetchStatistics() {
|
||||||
|
// 加载统计数据(只统计status=0,1的钢卷,且按当前查询条件过滤)
|
||||||
|
const params = {
|
||||||
|
enterCoilNo: this.queryParams.enterCoilNo || undefined,
|
||||||
|
currentCoilNo: this.queryParams.currentCoilNo || undefined,
|
||||||
|
warehouseId: this.queryParams.warehouseId
|
||||||
|
};
|
||||||
|
getPendingActionCoilCostStatistics(params).then(response => {
|
||||||
|
if (response && response.data) {
|
||||||
|
this.statistics = response.data;
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
console.warn('加载统计数据失败');
|
||||||
|
});
|
||||||
|
},
|
||||||
fetchPendingActionCoilCost() {
|
fetchPendingActionCoilCost() {
|
||||||
// 先尝试加载待操作钢卷的成本
|
// 加载列表数据(显示status=0,1,2的钢卷),分页由后端完成
|
||||||
listPendingActionCoilCost({ pageNum: 1, pageSize: 10000 }).then(response => {
|
const params = {
|
||||||
this.allCoilCostList = response.rows || [];
|
pageNum: this.queryParams.pageNum,
|
||||||
|
pageSize: this.queryParams.pageSize,
|
||||||
|
enterCoilNo: this.queryParams.enterCoilNo || undefined,
|
||||||
|
currentCoilNo: this.queryParams.currentCoilNo || undefined,
|
||||||
|
warehouseId: this.queryParams.warehouseId
|
||||||
|
};
|
||||||
|
listPendingActionCoilCost(params).then(response => {
|
||||||
|
this.coilCostList = response.rows || [];
|
||||||
|
this.total = response.total || 0;
|
||||||
this.dataSource = 'pending';
|
this.dataSource = 'pending';
|
||||||
this.applyFiltersAndPagination();
|
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
// 如果待操作接口失败,则加载日常分摊数据
|
// 如果待操作接口失败,则加载日常分摊数据
|
||||||
this.fetchDailyCoilCostData();
|
this.fetchDailyCoilCostData();
|
||||||
@@ -279,238 +309,67 @@ export default {
|
|||||||
},
|
},
|
||||||
fetchDailyCoilCostData() {
|
fetchDailyCoilCostData() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
listEnergyCoilDaily({ pageNum: 1, pageSize: 10000 }).then(response => {
|
const params = {
|
||||||
this.allCoilCostList = response.rows || [];
|
pageNum: this.queryParams.pageNum,
|
||||||
|
pageSize: this.queryParams.pageSize,
|
||||||
|
enterCoilNo: this.queryParams.enterCoilNo || undefined,
|
||||||
|
currentCoilNo: this.queryParams.currentCoilNo || undefined,
|
||||||
|
warehouseId: this.queryParams.warehouseId
|
||||||
|
};
|
||||||
|
listEnergyCoilDaily(params).then(response => {
|
||||||
|
this.coilCostList = response.rows || [];
|
||||||
|
this.total = response.total || 0;
|
||||||
this.dataSource = 'daily';
|
this.dataSource = 'daily';
|
||||||
this.applyFiltersAndPagination();
|
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
this.$message.error('加载钢卷能源成本失败');
|
this.$message.error('加载钢卷能源成本失败');
|
||||||
this.allCoilCostList = [];
|
this.coilCostList = [];
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
applyFiltersAndPagination() {
|
getAvgUnitCost() {
|
||||||
// 应用过滤条件
|
// 防止NaN显示,如果没有数据或消耗为0,显示0
|
||||||
let filteredList = this.allCoilCostList;
|
if (!this.statistics.avgUnitCost || isNaN(this.statistics.avgUnitCost)) {
|
||||||
|
return '0.00';
|
||||||
if (this.queryParams.enterCoilNo) {
|
|
||||||
filteredList = filteredList.filter(item =>
|
|
||||||
item.enterCoilNo && item.enterCoilNo.includes(this.queryParams.enterCoilNo)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
return this.formatNumber(this.statistics.avgUnitCost, 2);
|
||||||
if (this.queryParams.currentCoilNo) {
|
|
||||||
filteredList = filteredList.filter(item =>
|
|
||||||
item.currentCoilNo && item.currentCoilNo.includes(this.queryParams.currentCoilNo)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.queryParams.warehouseId) {
|
|
||||||
filteredList = filteredList.filter(item =>
|
|
||||||
item.warehouseId === this.queryParams.warehouseId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算统计数据
|
|
||||||
this.calculateStatistics(filteredList);
|
|
||||||
|
|
||||||
// 分页处理
|
|
||||||
this.total = filteredList.length;
|
|
||||||
const start = (this.queryParams.pageNum - 1) * this.queryParams.pageSize;
|
|
||||||
const end = start + this.queryParams.pageSize;
|
|
||||||
this.coilCostList = filteredList.slice(start, end);
|
|
||||||
},
|
|
||||||
calculateStatistics(list) {
|
|
||||||
this.statistics.totalCoils = list.length;
|
|
||||||
this.statistics.totalConsumption = list.reduce((sum, item) => sum + (item.consumptionQty || 0), 0);
|
|
||||||
this.statistics.totalCost = list.reduce((sum, item) => sum + (item.costAmount || 0), 0);
|
|
||||||
|
|
||||||
// 计算平均单位成本(总成本 / 总时长小时数)
|
|
||||||
const totalHours = list.reduce((sum, item) => sum + (item.productionDuration || 0) / 60, 0);
|
|
||||||
this.statistics.avgUnitCost = totalHours > 0 ? this.statistics.totalCost / totalHours : 0;
|
|
||||||
},
|
},
|
||||||
handleSearch() {
|
handleSearch() {
|
||||||
this.queryParams.pageNum = 1;
|
this.queryParams.pageNum = 1;
|
||||||
this.applyFiltersAndPagination();
|
this.fetchStatistics();
|
||||||
|
this.fetchPendingActionCoilCost();
|
||||||
},
|
},
|
||||||
handleReset() {
|
handleReset() {
|
||||||
this.queryParams.enterCoilNo = '';
|
this.queryParams.enterCoilNo = '';
|
||||||
this.queryParams.currentCoilNo = '';
|
this.queryParams.currentCoilNo = '';
|
||||||
this.queryParams.warehouseId = null;
|
this.queryParams.warehouseId = null;
|
||||||
this.queryParams.pageNum = 1;
|
this.queryParams.pageNum = 1;
|
||||||
this.applyFiltersAndPagination();
|
this.fetchStatistics();
|
||||||
|
this.fetchPendingActionCoilCost();
|
||||||
},
|
},
|
||||||
handlePageChange(pageNum) {
|
handlePageChange(pageNum) {
|
||||||
this.queryParams.pageNum = pageNum;
|
this.queryParams.pageNum = pageNum;
|
||||||
this.applyFiltersAndPagination();
|
this.fetchPendingActionCoilCost();
|
||||||
},
|
},
|
||||||
handlePageSizeChange(pageSize) {
|
handlePageSizeChange(pageSize) {
|
||||||
this.queryParams.pageSize = pageSize;
|
this.queryParams.pageSize = pageSize
|
||||||
this.queryParams.pageNum = 1;
|
this.queryParams.pageNum = 1
|
||||||
this.applyFiltersAndPagination();
|
this.fetchPendingActionCoilCost()
|
||||||
},
|
|
||||||
loadMockData() {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.coilCostList = [
|
|
||||||
{
|
|
||||||
coilCode: 'COIL-20231201-001',
|
|
||||||
logicWarehouseName: '库区A',
|
|
||||||
startTime: '2023-12-01 08:00:00',
|
|
||||||
endTime: '2023-12-01 16:30:00',
|
|
||||||
duration: 8.5,
|
|
||||||
totalCost: 1250.50,
|
|
||||||
energyCosts: [
|
|
||||||
{
|
|
||||||
energyTypeId: 1,
|
|
||||||
energyTypeName: '电',
|
|
||||||
consumption: 850,
|
|
||||||
consumptionUnit: 'kWh',
|
|
||||||
rate: 1.2,
|
|
||||||
cost: 1020,
|
|
||||||
percentage: 81.55
|
|
||||||
},
|
|
||||||
{
|
|
||||||
energyTypeId: 2,
|
|
||||||
energyTypeName: '水',
|
|
||||||
consumption: 120,
|
|
||||||
consumptionUnit: '吨',
|
|
||||||
rate: 1.5,
|
|
||||||
cost: 180,
|
|
||||||
percentage: 14.39
|
|
||||||
},
|
|
||||||
{
|
|
||||||
energyTypeId: 3,
|
|
||||||
energyTypeName: '气',
|
|
||||||
consumption: 45,
|
|
||||||
consumptionUnit: '立方米',
|
|
||||||
rate: 3.2,
|
|
||||||
cost: 50.5,
|
|
||||||
percentage: 4.04
|
|
||||||
}
|
|
||||||
],
|
|
||||||
relatedMeters: [
|
|
||||||
{
|
|
||||||
meterCode: 'METER-001',
|
|
||||||
energyTypeName: '电',
|
|
||||||
model: 'DL-2000',
|
|
||||||
manufacturer: '施耐德',
|
|
||||||
status: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
meterCode: 'METER-002',
|
|
||||||
energyTypeName: '水',
|
|
||||||
model: 'WM-500',
|
|
||||||
manufacturer: '西门子',
|
|
||||||
status: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
coilCode: 'COIL-20231201-002',
|
|
||||||
logicWarehouseName: '库区B',
|
|
||||||
startTime: '2023-12-01 09:00:00',
|
|
||||||
endTime: '2023-12-01 17:00:00',
|
|
||||||
duration: 8.0,
|
|
||||||
totalCost: 980.30,
|
|
||||||
energyCosts: [
|
|
||||||
{
|
|
||||||
energyTypeId: 1,
|
|
||||||
energyTypeName: '电',
|
|
||||||
consumption: 700,
|
|
||||||
consumptionUnit: 'kWh',
|
|
||||||
rate: 1.2,
|
|
||||||
cost: 840,
|
|
||||||
percentage: 85.75
|
|
||||||
},
|
|
||||||
{
|
|
||||||
energyTypeId: 2,
|
|
||||||
energyTypeName: '水',
|
|
||||||
consumption: 80,
|
|
||||||
consumptionUnit: '吨',
|
|
||||||
rate: 1.5,
|
|
||||||
cost: 120,
|
|
||||||
percentage: 12.24
|
|
||||||
},
|
|
||||||
{
|
|
||||||
energyTypeId: 3,
|
|
||||||
energyTypeName: '气',
|
|
||||||
consumption: 20,
|
|
||||||
consumptionUnit: '立方米',
|
|
||||||
rate: 3.2,
|
|
||||||
cost: 20.3,
|
|
||||||
percentage: 2.07
|
|
||||||
}
|
|
||||||
],
|
|
||||||
relatedMeters: [
|
|
||||||
{
|
|
||||||
meterCode: 'METER-003',
|
|
||||||
energyTypeName: '电',
|
|
||||||
model: 'DL-2000',
|
|
||||||
manufacturer: '施耐德',
|
|
||||||
status: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
coilCode: 'COIL-20231201-003',
|
|
||||||
logicWarehouseName: '库区C',
|
|
||||||
startTime: '2023-12-01 10:00:00',
|
|
||||||
endTime: '2023-12-01 18:30:00',
|
|
||||||
duration: 8.5,
|
|
||||||
totalCost: 1520.80,
|
|
||||||
energyCosts: [
|
|
||||||
{
|
|
||||||
energyTypeId: 1,
|
|
||||||
energyTypeName: '电',
|
|
||||||
consumption: 950,
|
|
||||||
consumptionUnit: 'kWh',
|
|
||||||
rate: 1.2,
|
|
||||||
cost: 1140,
|
|
||||||
percentage: 74.93
|
|
||||||
},
|
|
||||||
{
|
|
||||||
energyTypeId: 2,
|
|
||||||
energyTypeName: '水',
|
|
||||||
consumption: 150,
|
|
||||||
consumptionUnit: '吨',
|
|
||||||
rate: 1.5,
|
|
||||||
cost: 225,
|
|
||||||
percentage: 14.79
|
|
||||||
},
|
|
||||||
{
|
|
||||||
energyTypeId: 3,
|
|
||||||
energyTypeName: '气',
|
|
||||||
consumption: 80,
|
|
||||||
consumptionUnit: '立方米',
|
|
||||||
rate: 3.2,
|
|
||||||
cost: 155.8,
|
|
||||||
percentage: 10.24
|
|
||||||
}
|
|
||||||
],
|
|
||||||
relatedMeters: [
|
|
||||||
{
|
|
||||||
meterCode: 'METER-004',
|
|
||||||
energyTypeName: '电',
|
|
||||||
model: 'DL-3000',
|
|
||||||
manufacturer: '施耐德',
|
|
||||||
status: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
meterCode: 'METER-005',
|
|
||||||
energyTypeName: '水',
|
|
||||||
model: 'WM-600',
|
|
||||||
manufacturer: '西门子',
|
|
||||||
status: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}, 500);
|
|
||||||
},
|
},
|
||||||
showDetail(coil) {
|
showDetail(coil) {
|
||||||
this.selectedCoil = coil;
|
if (!coil || !coil.coilId) {
|
||||||
this.detailDialogVisible = true;
|
this.$message.warning('未找到钢卷ID')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.detailDialogVisible = true
|
||||||
|
this.selectedCoil = null
|
||||||
|
getPendingActionCoilCostDetail(coil.coilId)
|
||||||
|
.then(res => {
|
||||||
|
this.selectedCoil = res.data || coil
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.selectedCoil = coil
|
||||||
|
})
|
||||||
},
|
},
|
||||||
closeDetail() {
|
closeDetail() {
|
||||||
this.selectedCoil = null;
|
this.selectedCoil = null;
|
||||||
@@ -523,6 +382,41 @@ export default {
|
|||||||
formatNumber(value, decimals = 2) {
|
formatNumber(value, decimals = 2) {
|
||||||
if (value === null || value === undefined) return '0.00';
|
if (value === null || value === undefined) return '0.00';
|
||||||
return parseFloat(value).toFixed(decimals);
|
return parseFloat(value).toFixed(decimals);
|
||||||
|
},
|
||||||
|
formatDateTime(dateTime) {
|
||||||
|
if (!dateTime) return '-';
|
||||||
|
const date = new Date(dateTime);
|
||||||
|
return date.toLocaleString('zh-CN', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
formatDateOnly(dateTime) {
|
||||||
|
if (!dateTime) return '-';
|
||||||
|
const date = new Date(dateTime);
|
||||||
|
return date.toLocaleDateString('zh-CN', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadLatestMeterReadTime() {
|
||||||
|
getLatestMeterReadTime().then(response => {
|
||||||
|
if (response && response.data) {
|
||||||
|
this.statistics.latestMeterReadStartTime = response.data.startTime;
|
||||||
|
this.statistics.latestMeterReadEndTime = response.data.endTime;
|
||||||
|
console.log('最近一次抄表时间范围:', {
|
||||||
|
startTime: response.data.startTime,
|
||||||
|
endTime: response.data.endTime
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
console.warn('获取最近一次抄表时间失败');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -561,6 +455,12 @@ export default {
|
|||||||
&.cost-highlight {
|
&.cost-highlight {
|
||||||
color: #f56c6c;
|
color: #f56c6c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.time-range {
|
||||||
|
font-size: 32px;
|
||||||
|
color: #409eff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -571,10 +471,25 @@ export default {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
::v-deep .el-input,
|
.table-header-info {
|
||||||
::v-deep .el-select {
|
margin-bottom: 16px;
|
||||||
width: 100%;
|
padding: 12px 16px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-radius: 4px;
|
||||||
|
border-left: 3px solid #409eff;
|
||||||
|
|
||||||
|
.time-range-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-range-value {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,6 +503,11 @@ export default {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.no-data-tip {
|
||||||
|
color: #e6a23c;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.detail-content {
|
.detail-content {
|
||||||
.section {
|
.section {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
|||||||
419
klp-ui/src/views/ems/cost/cost.vue
Normal file
419
klp-ui/src/views/ems/cost/cost.vue
Normal file
@@ -0,0 +1,419 @@
|
|||||||
|
<template>
|
||||||
|
<div class="coil-total-cost-page">
|
||||||
|
<el-card class="search-card">
|
||||||
|
<el-form :model="queryParams" inline label-width="100px" size="small">
|
||||||
|
<el-form-item label="入场卷号" required>
|
||||||
|
<el-input v-model="queryParams.enterCoilNo" placeholder="必填,支持模糊"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="当前卷号">
|
||||||
|
<el-input v-model="queryParams.currentCoilNo" placeholder="支持模糊"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="开始日期">
|
||||||
|
<el-date-picker v-model="queryParams.startDate" type="date" value-format="yyyy-MM-dd" placeholder="开始日期"></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="结束日期">
|
||||||
|
<el-date-picker v-model="queryParams.endDate" type="date" value-format="yyyy-MM-dd" placeholder="结束日期"></el-date-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" @click="handleSearch">查询</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-row :gutter="20" class="summary-row">
|
||||||
|
<el-col :xs="24" :md="6">
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="label">能源成本</div>
|
||||||
|
<div class="value">¥ {{ formatNumber(energySummary.totalEnergyCost, 2) }}</div>
|
||||||
|
<div class="desc">耗能 {{ formatNumber(energySummary.totalConsumption, 2) }},时长 {{ formatNumber(energySummary.totalDuration, 1) }}h</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :md="6">
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="label">本日囤积成本</div>
|
||||||
|
<div class="value">¥ {{ formatNumber(stockSummary.todayCost, 2) }}</div>
|
||||||
|
<div class="desc">净重 {{ formatNumber(stockSummary.totalNetWeight, 2) }},毛重 {{ formatNumber(stockSummary.totalGrossWeight, 2) }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :md="6">
|
||||||
|
<div class="summary-card">
|
||||||
|
<div class="label">辅料成本</div>
|
||||||
|
<div class="value">¥ 0.00</div>
|
||||||
|
<div class="desc">暂不计</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :md="6">
|
||||||
|
<div class="summary-card highlight">
|
||||||
|
<div class="label">总成本</div>
|
||||||
|
<div class="value">¥ {{ formatNumber(totalCost, 2) }}</div>
|
||||||
|
<div class="desc">能源 + 囤积 + 辅料</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-card class="block-card">
|
||||||
|
<div slot="header" class="clearfix">
|
||||||
|
<span class="card-title">成本汇总(按入场卷号)</span>
|
||||||
|
</div>
|
||||||
|
<el-table :data="mergedRows" border stripe>
|
||||||
|
<el-table-column prop="enterCoilNo" label="入场卷号"></el-table-column>
|
||||||
|
<el-table-column prop="currentCoilNo" label="当前卷号"></el-table-column>
|
||||||
|
<el-table-column prop="energyCost" label="能源成本">
|
||||||
|
<template slot-scope="scope">¥ {{ formatNumber(scope.row.energyCost, 2) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="stockCost" label="囤积成本(当日)">
|
||||||
|
<template slot-scope="scope">¥ {{ formatNumber(scope.row.stockCost, 2) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="totalCost" label="总成本">
|
||||||
|
<template slot-scope="scope">¥ {{ formatNumber(scope.row.totalCost, 2) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="totalNetWeight" label="净重">
|
||||||
|
<template slot-scope="scope">{{ formatNumber(scope.row.totalNetWeight, 2) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="totalGrossWeight" label="毛重">
|
||||||
|
<template slot-scope="scope">{{ formatNumber(scope.row.totalGrossWeight, 2) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<el-button type="text" size="mini" @click="openDetail(scope.row)">查看详情</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="table-actions">
|
||||||
|
<el-button size="mini" type="primary" icon="el-icon-download" @click="exportSummary">导出汇总</el-button>
|
||||||
|
</div>
|
||||||
|
<pagination
|
||||||
|
v-show="mergedTotal > 0"
|
||||||
|
:total="mergedTotal"
|
||||||
|
:page.sync="mergedQuery.pageNum"
|
||||||
|
:limit.sync="mergedQuery.pageSize"
|
||||||
|
@pagination="loadMerged"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog title="钢卷成本详情" :visible.sync="detailVisible" width="80%" :close-on-click-modal="false">
|
||||||
|
<div class="detail-section">
|
||||||
|
<div class="detail-title">能源分摊明细({{ detailEnterCoilNo }})</div>
|
||||||
|
<el-table :data="detailEnergyRows" stripe border max-height="300">
|
||||||
|
<el-table-column prop="enterCoilNo" label="入场卷号" width="140"></el-table-column>
|
||||||
|
<el-table-column prop="currentCoilNo" label="当前卷号" width="140"></el-table-column>
|
||||||
|
<el-table-column prop="warehouseName" label="库区" width="140"></el-table-column>
|
||||||
|
<el-table-column prop="duration" label="时长(h)" width="100">
|
||||||
|
<template slot-scope="scope">{{ formatNumber(scope.row.duration, 2) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="consumption" label="分摊能耗" width="120">
|
||||||
|
<template slot-scope="scope">{{ formatNumber(scope.row.consumption, 4) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="totalCost" label="分摊成本" width="120">
|
||||||
|
<template slot-scope="scope">¥ {{ formatNumber(scope.row.totalCost, 2) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="startTime" label="开始时间" width="180">
|
||||||
|
<template slot-scope="scope">{{ formatTime(scope.row.startTime) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="endTime" label="结束时间" width="180">
|
||||||
|
<template slot-scope="scope">{{ formatTime(scope.row.endTime) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detail-section">
|
||||||
|
<div class="detail-title">囤积成本({{ detailEnterCoilNo }})</div>
|
||||||
|
<el-table :data="detailStockRow ? [detailStockRow] : []" stripe border>
|
||||||
|
<el-table-column prop="enterCoilNo" label="入场卷号"></el-table-column>
|
||||||
|
<el-table-column prop="totalNetWeight" label="净重">
|
||||||
|
<template slot-scope="scope">{{ formatNumber(scope.row.totalNetWeight, 2) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="totalGrossWeight" label="毛重">
|
||||||
|
<template slot-scope="scope">{{ formatNumber(scope.row.totalGrossWeight, 2) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="avgStorageDays" label="平均在库天数">
|
||||||
|
<template slot-scope="scope">{{ formatNumber(scope.row.avgStorageDays, 1) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="todayCost" label="本日囤积成本">
|
||||||
|
<template slot-scope="scope">¥ {{ formatNumber(scope.row.todayCost != null ? scope.row.todayCost : scope.row.totalCost, 2) }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="detailVisible = false">关 闭</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { fetchCoilTotalEnergySummary, fetchCoilTotalEnergyDetail } from '@/api/ems/energyCostReport'
|
||||||
|
import { fetchCoilTotalMerged } from '@/api/ems/energyCostReport'
|
||||||
|
import { exportCoilTotalMerged } from '@/api/ems/energyCostReport'
|
||||||
|
import { getStockpileCostList } from '@/api/wms/cost'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CoilTotalCost',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
queryParams: {
|
||||||
|
enterCoilNo: '',
|
||||||
|
currentCoilNo: '',
|
||||||
|
startDate: undefined,
|
||||||
|
endDate: undefined
|
||||||
|
},
|
||||||
|
energyQuery: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 50
|
||||||
|
},
|
||||||
|
mergedQuery: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 50
|
||||||
|
},
|
||||||
|
stockQuery: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 50
|
||||||
|
},
|
||||||
|
energySummary: {
|
||||||
|
coilCount: 0,
|
||||||
|
totalDuration: 0,
|
||||||
|
totalConsumption: 0,
|
||||||
|
totalEnergyCost: 0
|
||||||
|
},
|
||||||
|
energyDetail: {
|
||||||
|
rows: [],
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
mergedRows: [],
|
||||||
|
mergedTotal: 0,
|
||||||
|
stockDetail: {
|
||||||
|
rows: [],
|
||||||
|
total: 0
|
||||||
|
},
|
||||||
|
stockSummary: {
|
||||||
|
totalCost: 0,
|
||||||
|
todayCost: 0,
|
||||||
|
totalNetWeight: 0,
|
||||||
|
totalGrossWeight: 0
|
||||||
|
},
|
||||||
|
energyLoading: false,
|
||||||
|
stockLoading: false,
|
||||||
|
detailVisible: false,
|
||||||
|
detailEnterCoilNo: '',
|
||||||
|
detailEnergyRows: [],
|
||||||
|
detailStockRow: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
totalCost() {
|
||||||
|
const energy = Number(this.energySummary.totalEnergyCost) || 0
|
||||||
|
const stock = Number(this.stockSummary.todayCost ?? this.stockSummary.totalCost) || 0
|
||||||
|
return energy + stock
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.handleSearch()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleSearch() {
|
||||||
|
this.energyQuery.pageNum = 1
|
||||||
|
this.mergedQuery.pageNum = 1
|
||||||
|
this.stockQuery.pageNum = 1
|
||||||
|
this.loadEnergy()
|
||||||
|
this.loadMerged()
|
||||||
|
this.loadStockDetail()
|
||||||
|
},
|
||||||
|
handleReset() {
|
||||||
|
this.queryParams = {
|
||||||
|
enterCoilNo: '',
|
||||||
|
currentCoilNo: '',
|
||||||
|
startDate: undefined,
|
||||||
|
endDate: undefined
|
||||||
|
}
|
||||||
|
this.energyQuery.pageNum = 1
|
||||||
|
this.mergedQuery.pageNum = 1
|
||||||
|
this.stockQuery.pageNum = 1
|
||||||
|
this.energySummary = { coilCount: 0, totalDuration: 0, totalConsumption: 0, totalEnergyCost: 0 }
|
||||||
|
this.energyDetail = { rows: [], total: 0 }
|
||||||
|
this.mergedRows = []
|
||||||
|
this.mergedTotal = 0
|
||||||
|
this.stockDetail = { rows: [], total: 0 }
|
||||||
|
this.stockSummary = { totalCost: 0, totalNetWeight: 0, totalGrossWeight: 0 }
|
||||||
|
this.handleSearch()
|
||||||
|
},
|
||||||
|
loadMerged() {
|
||||||
|
const params = {
|
||||||
|
...this.queryParams,
|
||||||
|
pageNum: this.mergedQuery.pageNum,
|
||||||
|
pageSize: this.mergedQuery.pageSize
|
||||||
|
}
|
||||||
|
fetchCoilTotalMerged(params).then(res => {
|
||||||
|
this.mergedRows = res.rows || []
|
||||||
|
this.mergedTotal = res.total || 0
|
||||||
|
})
|
||||||
|
},
|
||||||
|
loadEnergy() {
|
||||||
|
this.energyLoading = true
|
||||||
|
const params = { ...this.queryParams }
|
||||||
|
fetchCoilTotalEnergySummary(params).then(res => {
|
||||||
|
this.energySummary = res.data || this.energySummary
|
||||||
|
}).finally(() => {
|
||||||
|
this.loadEnergyDetail()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
loadEnergyDetail() {
|
||||||
|
const params = {
|
||||||
|
...this.queryParams,
|
||||||
|
pageNum: this.energyQuery.pageNum,
|
||||||
|
pageSize: this.energyQuery.pageSize
|
||||||
|
}
|
||||||
|
fetchCoilTotalEnergyDetail(params).then(res => {
|
||||||
|
this.energyDetail = {
|
||||||
|
rows: res.rows || [],
|
||||||
|
total: res.total || 0
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.energyLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
loadStockDetail() {
|
||||||
|
this.stockLoading = true
|
||||||
|
const params = {
|
||||||
|
enterCoilNo: this.queryParams.enterCoilNo,
|
||||||
|
currentCoilNo: this.queryParams.currentCoilNo,
|
||||||
|
pageNum: this.stockQuery.pageNum,
|
||||||
|
pageSize: this.stockQuery.pageSize
|
||||||
|
}
|
||||||
|
getStockpileCostList(params).then(res => {
|
||||||
|
const data = res.data || {}
|
||||||
|
this.stockDetail = {
|
||||||
|
rows: data.rows || [],
|
||||||
|
total: data.total || 0
|
||||||
|
}
|
||||||
|
const summary = data.summary || {}
|
||||||
|
this.stockSummary = {
|
||||||
|
totalCost: summary.totalCost ?? 0,
|
||||||
|
todayCost: summary.todayCost ?? summary.totalCost ?? 0,
|
||||||
|
totalNetWeight: summary.totalNetWeight ?? 0,
|
||||||
|
totalGrossWeight: summary.totalGrossWeight ?? 0
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.stockLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
openDetail(row) {
|
||||||
|
if (!row || !row.enterCoilNo) return
|
||||||
|
this.detailEnterCoilNo = row.enterCoilNo
|
||||||
|
this.detailVisible = true
|
||||||
|
this.loadDetailEnergy(row.enterCoilNo)
|
||||||
|
this.loadDetailStock(row.enterCoilNo)
|
||||||
|
},
|
||||||
|
loadDetailEnergy(enterCoilNo) {
|
||||||
|
const params = {
|
||||||
|
enterCoilNo,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 200
|
||||||
|
}
|
||||||
|
fetchCoilTotalEnergyDetail(params).then(res => {
|
||||||
|
this.detailEnergyRows = res.rows || []
|
||||||
|
})
|
||||||
|
},
|
||||||
|
loadDetailStock(enterCoilNo) {
|
||||||
|
const params = {
|
||||||
|
enterCoilNo,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 1
|
||||||
|
}
|
||||||
|
getStockpileCostList(params).then(res => {
|
||||||
|
const data = res.data || {}
|
||||||
|
const rows = data.rows || []
|
||||||
|
this.detailStockRow = rows.find(item => item.enterCoilNo === enterCoilNo) || null
|
||||||
|
})
|
||||||
|
},
|
||||||
|
exportSummary() {
|
||||||
|
const params = { ...this.queryParams }
|
||||||
|
exportCoilTotalMerged(params).then(res => {
|
||||||
|
const blob = new Blob([res], { type: 'application/vnd.ms-excel' })
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = 'coil_cost_summary.xlsx'
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
formatNumber(val, digits = 2) {
|
||||||
|
const num = Number(val)
|
||||||
|
if (isNaN(num)) return '0'.padEnd(2 + digits, '0')
|
||||||
|
return num.toFixed(digits)
|
||||||
|
},
|
||||||
|
formatTime(val) {
|
||||||
|
if (!val) return '-'
|
||||||
|
const date = new Date(val)
|
||||||
|
if (Number.isNaN(date.getTime())) return val
|
||||||
|
return date.toLocaleString('zh-CN')
|
||||||
|
},
|
||||||
|
formatStatus(status) {
|
||||||
|
const map = { 0: '在产', 1: '在产', 2: '完成' }
|
||||||
|
return map[status] || '-'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.coil-total-cost-page {
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
.search-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-row {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.summary-card {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 16px;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04);
|
||||||
|
.label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 26px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
.desc {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
&.highlight {
|
||||||
|
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.sub {
|
||||||
|
margin-left: 12px;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.cost {
|
||||||
|
color: #f56c6c;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,572 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="energy-cost-summary-page">
|
|
||||||
<!-- 查询条件 -->
|
|
||||||
<el-card class="search-card">
|
|
||||||
<el-form :model="queryParams" label-width="100px" size="small">
|
|
||||||
<el-row :gutter="20">
|
|
||||||
<el-col :xs="24" :sm="12" :md="6">
|
|
||||||
<el-form-item label="统计维度:">
|
|
||||||
<el-select v-model="queryParams.dimension" placeholder="请选择维度" @change="handleQuery">
|
|
||||||
<el-option label="按钢卷" value="coil"></el-option>
|
|
||||||
<el-option label="按库区" value="warehouse"></el-option>
|
|
||||||
<el-option label="按时间" value="time"></el-option>
|
|
||||||
<el-option label="按能源" value="energy"></el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :xs="24" :sm="12" :md="6">
|
|
||||||
<el-form-item label="开始日期:">
|
|
||||||
<el-date-picker v-model="queryParams.startDate" type="date" placeholder="选择开始日期" value-format="yyyy-MM-dd" @change="handleQuery"></el-date-picker>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :xs="24" :sm="12" :md="6">
|
|
||||||
<el-form-item label="结束日期:">
|
|
||||||
<el-date-picker v-model="queryParams.endDate" type="date" placeholder="选择结束日期" value-format="yyyy-MM-dd" @change="handleQuery"></el-date-picker>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :xs="24" :sm="12" :md="6">
|
|
||||||
<el-button type="primary" icon="el-icon-search" size="small" @click="handleQuery">查询</el-button>
|
|
||||||
<el-button icon="el-icon-refresh" size="small" @click="resetQuery">重置</el-button>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-form>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<!-- 按钢卷统计 -->
|
|
||||||
<div v-if="queryParams.dimension === 'coil'">
|
|
||||||
<el-card class="summary-card" v-loading="loading">
|
|
||||||
<div slot="header" class="clearfix">
|
|
||||||
<span class="card-title">钢卷能源成本汇总</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-table :data="coilSummaryList" stripe border max-height="600">
|
|
||||||
<el-table-column prop="coilCode" label="钢卷编号" width="150"></el-table-column>
|
|
||||||
<el-table-column prop="logicWarehouseName" label="逻辑库区" width="120"></el-table-column>
|
|
||||||
<el-table-column prop="startTime" label="开始时间" width="180">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
{{ formatTime(scope.row.startTime) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="endTime" label="完成时间" width="180">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
{{ formatTime(scope.row.endTime) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="duration" label="生产时长" width="100">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
{{ formatNumber(scope.row.duration, 1) }}h
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="totalCost" label="总成本" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span class="cost-value">¥ {{ formatNumber(scope.row.totalCost, 2) }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="unitCost" label="单位成本" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
¥ {{ formatNumber(scope.row.unitCost, 2) }}/h
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="100" fixed="right">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button type="primary" size="mini" @click="viewCoilDetail(scope.row)">详情</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 按库区统计 -->
|
|
||||||
<div v-if="queryParams.dimension === 'warehouse'">
|
|
||||||
<el-card class="summary-card" v-loading="loading">
|
|
||||||
<div slot="header" class="clearfix">
|
|
||||||
<span class="card-title">库区能源成本汇总</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-table :data="warehouseSummaryList" stripe border max-height="600">
|
|
||||||
<el-table-column prop="logicWarehouseName" label="逻辑库区" width="150"></el-table-column>
|
|
||||||
<el-table-column prop="coilCount" label="生产钢卷数" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
{{ scope.row.coilCount }} 卷
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="totalDuration" label="总生产时长" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
{{ formatNumber(scope.row.totalDuration, 1) }}h
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="totalCost" label="总成本" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span class="cost-value">¥ {{ formatNumber(scope.row.totalCost, 2) }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="averageUnitCost" label="平均单位成本" width="140">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
¥ {{ formatNumber(scope.row.averageUnitCost, 2) }}/卷
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="100" fixed="right">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<el-button type="primary" size="mini" @click="viewWarehouseDetail(scope.row)">详情</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 按时间统计 -->
|
|
||||||
<div v-if="queryParams.dimension === 'time'">
|
|
||||||
<el-card class="chart-card" v-loading="loading">
|
|
||||||
<div slot="header" class="clearfix">
|
|
||||||
<span class="card-title">能源成本趋势</span>
|
|
||||||
</div>
|
|
||||||
<div id="costTrendChart" style="height: 400px;"></div>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card class="summary-card" v-loading="loading">
|
|
||||||
<div slot="header" class="clearfix">
|
|
||||||
<span class="card-title">按日期统计</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-table :data="timeSummaryList" stripe border max-height="600">
|
|
||||||
<el-table-column prop="date" label="日期" width="150"></el-table-column>
|
|
||||||
<el-table-column prop="coilCount" label="生产钢卷数" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
{{ scope.row.coilCount }} 卷
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="totalDuration" label="总生产时长" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
{{ formatNumber(scope.row.totalDuration, 1) }}h
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="totalCost" label="总成本" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span class="cost-value">¥ {{ formatNumber(scope.row.totalCost, 2) }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="averageUnitCost" label="平均单位成本" width="140">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
¥ {{ formatNumber(scope.row.averageUnitCost, 2) }}/卷
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 按能源统计 -->
|
|
||||||
<div v-if="queryParams.dimension === 'energy'">
|
|
||||||
<el-card class="chart-card" v-loading="loading">
|
|
||||||
<div slot="header" class="clearfix">
|
|
||||||
<span class="card-title">能源成本占比</span>
|
|
||||||
</div>
|
|
||||||
<div id="energyCostPieChart" style="height: 400px;"></div>
|
|
||||||
</el-card>
|
|
||||||
|
|
||||||
<el-card class="summary-card" v-loading="loading">
|
|
||||||
<div slot="header" class="clearfix">
|
|
||||||
<span class="card-title">能源成本明细</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-table :data="energySummaryList" stripe border max-height="600">
|
|
||||||
<el-table-column prop="energyTypeName" label="能源类型" width="150"></el-table-column>
|
|
||||||
<el-table-column prop="totalConsumption" label="总消耗量" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
{{ formatNumber(scope.row.totalConsumption, 2) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="consumptionUnit" label="单位" width="100"></el-table-column>
|
|
||||||
<el-table-column prop="totalCost" label="总成本" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
<span class="cost-value">¥ {{ formatNumber(scope.row.totalCost, 2) }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="percentage" label="成本占比" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
{{ formatNumber(scope.row.percentage, 2) }}%
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="averageRate" label="平均费率" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
¥ {{ formatNumber(scope.row.averageRate, 4) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import * as echarts from 'echarts'
|
|
||||||
import { listEnergyCoilDaily } from '@/api/ems/energyAllocation'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'EnergyCostSummary',
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: false,
|
|
||||||
queryParams: {
|
|
||||||
dimension: 'coil',
|
|
||||||
startDate: '',
|
|
||||||
endDate: '',
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 100
|
|
||||||
},
|
|
||||||
coilSummaryList: [],
|
|
||||||
warehouseSummaryList: [],
|
|
||||||
timeSummaryList: [],
|
|
||||||
energySummaryList: [],
|
|
||||||
chartInstances: {}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.handleQuery();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleQuery() {
|
|
||||||
this.loading = true;
|
|
||||||
// 调用后端API获取钢卷能源成本数据
|
|
||||||
listEnergyCoilDaily(this.queryParams).then(response => {
|
|
||||||
const data = response.rows || [];
|
|
||||||
// 根据选择的维度处理数据
|
|
||||||
if (this.queryParams.dimension === 'coil') {
|
|
||||||
this.coilSummaryList = data;
|
|
||||||
} else {
|
|
||||||
this.processDataByDimension(data);
|
|
||||||
}
|
|
||||||
this.initCharts();
|
|
||||||
}).catch(() => {
|
|
||||||
this.$message.error('加载能源成本数据失败');
|
|
||||||
// 加载模拟数据作为备用
|
|
||||||
this.loadMockData();
|
|
||||||
}).finally(() => {
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
processDataByDimension(data) {
|
|
||||||
if (this.queryParams.dimension === 'warehouse') {
|
|
||||||
// 按库区聚合
|
|
||||||
const warehouseMap = {};
|
|
||||||
data.forEach(item => {
|
|
||||||
const key = item.warehouseId;
|
|
||||||
if (!warehouseMap[key]) {
|
|
||||||
warehouseMap[key] = {
|
|
||||||
warehouseId: item.warehouseId,
|
|
||||||
logicWarehouseName: item.logicWarehouseName,
|
|
||||||
coilCount: 0,
|
|
||||||
totalDuration: 0,
|
|
||||||
totalCost: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
warehouseMap[key].coilCount++;
|
|
||||||
warehouseMap[key].totalDuration += (item.allocationBasisDays || 0);
|
|
||||||
warehouseMap[key].totalCost += (item.costAmount || 0);
|
|
||||||
});
|
|
||||||
this.warehouseSummaryList = Object.values(warehouseMap).map(w => ({
|
|
||||||
...w,
|
|
||||||
averageUnitCost: w.coilCount > 0 ? (w.totalCost / w.coilCount).toFixed(2) : 0
|
|
||||||
}));
|
|
||||||
} else if (this.queryParams.dimension === 'time') {
|
|
||||||
// 按时间聚合
|
|
||||||
const timeMap = {};
|
|
||||||
data.forEach(item => {
|
|
||||||
const date = item.calcDate;
|
|
||||||
if (!timeMap[date]) {
|
|
||||||
timeMap[date] = {
|
|
||||||
date: date,
|
|
||||||
coilCount: 0,
|
|
||||||
totalDuration: 0,
|
|
||||||
totalCost: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
timeMap[date].coilCount++;
|
|
||||||
timeMap[date].totalDuration += (item.allocationBasisDays || 0);
|
|
||||||
timeMap[date].totalCost += (item.costAmount || 0);
|
|
||||||
});
|
|
||||||
this.timeSummaryList = Object.values(timeMap).map(t => ({
|
|
||||||
...t,
|
|
||||||
averageUnitCost: t.coilCount > 0 ? (t.totalCost / t.coilCount).toFixed(2) : 0
|
|
||||||
}));
|
|
||||||
} else if (this.queryParams.dimension === 'energy') {
|
|
||||||
// 按能源类型聚合
|
|
||||||
const energyMap = {};
|
|
||||||
data.forEach(item => {
|
|
||||||
const key = item.energyTypeId;
|
|
||||||
if (!energyMap[key]) {
|
|
||||||
energyMap[key] = {
|
|
||||||
energyTypeId: item.energyTypeId,
|
|
||||||
energyTypeName: item.energyTypeName,
|
|
||||||
totalConsumption: 0,
|
|
||||||
totalCost: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
energyMap[key].totalConsumption += (item.consumptionQty || 0);
|
|
||||||
energyMap[key].totalCost += (item.costAmount || 0);
|
|
||||||
});
|
|
||||||
const totalCost = Object.values(energyMap).reduce((sum, e) => sum + e.totalCost, 0);
|
|
||||||
this.energySummaryList = Object.values(energyMap).map(e => ({
|
|
||||||
...e,
|
|
||||||
percentage: totalCost > 0 ? ((e.totalCost / totalCost) * 100).toFixed(2) : 0,
|
|
||||||
averageRate: e.totalConsumption > 0 ? (e.totalCost / e.totalConsumption).toFixed(4) : 0
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
loadMockData() {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.coilSummaryList = [
|
|
||||||
{
|
|
||||||
coilCode: 'COIL-20231201-001',
|
|
||||||
logicWarehouseName: '库区A',
|
|
||||||
startTime: '2023-12-01 08:00:00',
|
|
||||||
endTime: '2023-12-01 16:30:00',
|
|
||||||
duration: 8.5,
|
|
||||||
totalCost: 1250.50,
|
|
||||||
unitCost: 147.12
|
|
||||||
},
|
|
||||||
{
|
|
||||||
coilCode: 'COIL-20231201-002',
|
|
||||||
logicWarehouseName: '库区A',
|
|
||||||
startTime: '2023-12-01 17:00:00',
|
|
||||||
endTime: '2023-12-02 01:30:00',
|
|
||||||
duration: 8.5,
|
|
||||||
totalCost: 1248.75,
|
|
||||||
unitCost: 146.88
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
this.warehouseSummaryList = [
|
|
||||||
{
|
|
||||||
logicWarehouseName: '库区A',
|
|
||||||
coilCount: 45,
|
|
||||||
totalDuration: 382.5,
|
|
||||||
totalCost: 56325.50,
|
|
||||||
averageUnitCost: 1251.67
|
|
||||||
},
|
|
||||||
{
|
|
||||||
logicWarehouseName: '库区B',
|
|
||||||
coilCount: 38,
|
|
||||||
totalDuration: 323.0,
|
|
||||||
totalCost: 47850.25,
|
|
||||||
averageUnitCost: 1259.22
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
this.timeSummaryList = [
|
|
||||||
{
|
|
||||||
date: '2023-12-01',
|
|
||||||
coilCount: 12,
|
|
||||||
totalDuration: 102.0,
|
|
||||||
totalCost: 15125.50,
|
|
||||||
averageUnitCost: 1260.46
|
|
||||||
},
|
|
||||||
{
|
|
||||||
date: '2023-12-02',
|
|
||||||
coilCount: 15,
|
|
||||||
totalDuration: 127.5,
|
|
||||||
totalCost: 18950.75,
|
|
||||||
averageUnitCost: 1263.38
|
|
||||||
},
|
|
||||||
{
|
|
||||||
date: '2023-12-03',
|
|
||||||
coilCount: 18,
|
|
||||||
totalDuration: 153.0,
|
|
||||||
totalCost: 22750.25,
|
|
||||||
averageUnitCost: 1263.91
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
this.energySummaryList = [
|
|
||||||
{
|
|
||||||
energyTypeId: 1,
|
|
||||||
energyTypeName: '电',
|
|
||||||
totalConsumption: 32500,
|
|
||||||
consumptionUnit: 'kWh',
|
|
||||||
totalCost: 45875,
|
|
||||||
percentage: 81.45,
|
|
||||||
averageRate: 1.4115
|
|
||||||
},
|
|
||||||
{
|
|
||||||
energyTypeId: 2,
|
|
||||||
energyTypeName: '水',
|
|
||||||
totalConsumption: 4200,
|
|
||||||
consumptionUnit: '吨',
|
|
||||||
totalCost: 8400,
|
|
||||||
percentage: 14.91,
|
|
||||||
averageRate: 2.0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
energyTypeId: 3,
|
|
||||||
energyTypeName: '气',
|
|
||||||
totalConsumption: 1550,
|
|
||||||
consumptionUnit: '立方米',
|
|
||||||
totalCost: 2050.5,
|
|
||||||
percentage: 3.64,
|
|
||||||
averageRate: 1.3229
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.initCharts();
|
|
||||||
});
|
|
||||||
}, 500);
|
|
||||||
},
|
|
||||||
resetQuery() {
|
|
||||||
this.queryParams = {
|
|
||||||
dimension: 'coil',
|
|
||||||
startDate: '',
|
|
||||||
endDate: ''
|
|
||||||
};
|
|
||||||
this.handleQuery();
|
|
||||||
},
|
|
||||||
initCharts() {
|
|
||||||
if (this.queryParams.dimension === 'time') {
|
|
||||||
this.initCostTrendChart();
|
|
||||||
} else if (this.queryParams.dimension === 'energy') {
|
|
||||||
this.initEnergyCostPieChart();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
initCostTrendChart() {
|
|
||||||
const chartDom = document.getElementById('costTrendChart');
|
|
||||||
if (!chartDom) return;
|
|
||||||
|
|
||||||
const instance = echarts.init(chartDom);
|
|
||||||
this.chartInstances.trend = instance;
|
|
||||||
|
|
||||||
const option = {
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis'
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
data: ['总成本', '平均单位成本']
|
|
||||||
},
|
|
||||||
xAxis: {
|
|
||||||
type: 'category',
|
|
||||||
data: this.timeSummaryList.map(item => item.date)
|
|
||||||
},
|
|
||||||
yAxis: [
|
|
||||||
{
|
|
||||||
type: 'value',
|
|
||||||
name: '总成本',
|
|
||||||
axisLabel: {
|
|
||||||
formatter: '¥{value}'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'value',
|
|
||||||
name: '平均单位成本',
|
|
||||||
axisLabel: {
|
|
||||||
formatter: '¥{value}'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '总成本',
|
|
||||||
type: 'bar',
|
|
||||||
data: this.timeSummaryList.map(item => item.totalCost),
|
|
||||||
yAxisIndex: 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '平均单位成本',
|
|
||||||
type: 'line',
|
|
||||||
data: this.timeSummaryList.map(item => item.averageUnitCost),
|
|
||||||
yAxisIndex: 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
instance.setOption(option);
|
|
||||||
},
|
|
||||||
initEnergyCostPieChart() {
|
|
||||||
const chartDom = document.getElementById('energyCostPieChart');
|
|
||||||
if (!chartDom) return;
|
|
||||||
|
|
||||||
const instance = echarts.init(chartDom);
|
|
||||||
this.chartInstances.energy = instance;
|
|
||||||
|
|
||||||
const option = {
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'item',
|
|
||||||
formatter: '{b}: ¥{c} ({d}%)'
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
orient: 'vertical',
|
|
||||||
left: 'left'
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '能源成本',
|
|
||||||
type: 'pie',
|
|
||||||
radius: '50%',
|
|
||||||
data: this.energySummaryList.map(item => ({
|
|
||||||
value: item.totalCost,
|
|
||||||
name: item.energyTypeName
|
|
||||||
})),
|
|
||||||
emphasis: {
|
|
||||||
itemStyle: {
|
|
||||||
shadowBlur: 10,
|
|
||||||
shadowOffsetX: 0,
|
|
||||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
instance.setOption(option);
|
|
||||||
},
|
|
||||||
formatTime(time) {
|
|
||||||
if (!time) return '-';
|
|
||||||
return new Date(time).toLocaleString('zh-CN');
|
|
||||||
},
|
|
||||||
formatNumber(value, decimals = 2) {
|
|
||||||
if (value === null || value === undefined) return '0.00';
|
|
||||||
return parseFloat(value).toFixed(decimals);
|
|
||||||
},
|
|
||||||
viewCoilDetail(row) {
|
|
||||||
this.$router.push({
|
|
||||||
path: '/ems/cost/coilCost',
|
|
||||||
query: { coilId: row.coilId }
|
|
||||||
});
|
|
||||||
},
|
|
||||||
viewWarehouseDetail(row) {
|
|
||||||
this.$router.push({
|
|
||||||
path: '/ems/cost/warehouseProduction',
|
|
||||||
query: { logicWarehouseId: row.logicWarehouseId }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
Object.values(this.chartInstances).forEach(instance => {
|
|
||||||
instance?.dispose();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.energy-cost-summary-page {
|
|
||||||
padding: 20px;
|
|
||||||
|
|
||||||
.search-card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chart-card,
|
|
||||||
.summary-card {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
.card-title {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #303133;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cost-value {
|
|
||||||
color: #f56c6c;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -88,24 +88,24 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table :data="energyBreakdown" stripe border>
|
<el-table :data="energyBreakdown" stripe border>
|
||||||
<el-table-column prop="energyTypeName" label="能源类型" width="150"></el-table-column>
|
<el-table-column prop="energyTypeName" label="能源类型" ></el-table-column>
|
||||||
<el-table-column prop="consumption" label="总消耗量" width="120">
|
<el-table-column prop="consumption" label="总消耗量" >
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{ formatNumber(scope.row.consumption, 2) }}
|
{{ formatNumber(scope.row.consumption, 2) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="consumptionUnit" label="单位" width="100"></el-table-column>
|
<el-table-column prop="consumptionUnit" label="单位" ></el-table-column>
|
||||||
<el-table-column prop="cost" label="总成本" width="120">
|
<el-table-column prop="cost" label="总成本" >
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span class="cost-value">¥ {{ formatNumber(scope.row.cost, 2) }}</span>
|
<span class="cost-value">¥ {{ formatNumber(scope.row.cost, 2) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="percentage" label="成本占比" width="120">
|
<el-table-column prop="percentage" label="成本占比">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{ formatNumber(scope.row.percentage, 2) }}%
|
{{ formatNumber(scope.row.percentage, 2) }}%
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="averageUnitCost" label="单位成本" width="120">
|
<el-table-column prop="averageUnitCost" label="单位成本" >
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
¥ {{ formatNumber(scope.row.averageUnitCost, 4) }}/单位
|
¥ {{ formatNumber(scope.row.averageUnitCost, 4) }}/单位
|
||||||
</template>
|
</template>
|
||||||
@@ -117,44 +117,77 @@
|
|||||||
<el-card class="coil-list-card" v-loading="loading">
|
<el-card class="coil-list-card" v-loading="loading">
|
||||||
<div slot="header" class="clearfix">
|
<div slot="header" class="clearfix">
|
||||||
<span class="card-title">该库区生产的钢卷列表</span>
|
<span class="card-title">该库区生产的钢卷列表</span>
|
||||||
<span class="count">(共 {{ coilList.length }} 卷)</span>
|
<span class="count">(共 {{ total }} 卷)</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table :data="coilList" stripe border max-height="500">
|
<el-table :data="coilList" stripe border max-height="500">
|
||||||
<el-table-column prop="coilCode" label="钢卷编号" width="150"></el-table-column>
|
<el-table-column prop="coilCode" label="钢卷编号" ></el-table-column>
|
||||||
<el-table-column prop="startTime" label="开始时间" width="180">
|
<el-table-column prop="startTime" label="开始时间" >
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{ formatTime(scope.row.startTime) }}
|
{{ formatTime(scope.row.startTime) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="endTime" label="完成时间" width="180">
|
<el-table-column prop="endTime" label="完成时间" >
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{ formatTime(scope.row.endTime) }}
|
{{ formatTime(scope.row.endTime) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="duration" label="生产时长" width="100">
|
<el-table-column prop="duration" label="生产时长">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{ formatNumber(scope.row.duration, 1) }}h
|
{{ formatNumber(scope.row.duration, 1) }}h
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="totalCost" label="能源成本" width="120">
|
<el-table-column prop="totalCost" label="能源成本">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<span class="cost-value">¥ {{ formatNumber(scope.row.totalCost, 2) }}</span>
|
<span class="cost-value">¥ {{ formatNumber(scope.row.totalCost, 2) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="100" fixed="right">
|
<el-table-column label="操作"fixed="right">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button type="primary" size="mini" @click="viewCoilDetail(scope.row)">查看详情</el-button>
|
<el-button type="primary" size="mini" @click="viewCoilDetail(scope.row)">查看详情</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
<pagination
|
||||||
|
v-show="total > 0"
|
||||||
|
:total="total"
|
||||||
|
:page.sync="queryParams.pageNum"
|
||||||
|
:limit.sync="queryParams.pageSize"
|
||||||
|
@pagination="handleQuery"
|
||||||
|
/>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 钢卷详情弹窗 -->
|
||||||
|
<el-dialog
|
||||||
|
title="钢卷详情"
|
||||||
|
:visible.sync="coilDetailVisible"
|
||||||
|
width="600px"
|
||||||
|
:close-on-click-modal="false">
|
||||||
|
<el-skeleton :loading="coilDetailLoading" animated>
|
||||||
|
<el-descriptions :column="2" size="small" border>
|
||||||
|
<el-descriptions-item label="钢卷ID">{{ coilDetail.coilId || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="状态">{{ formatActionStatus(coilDetail.actionStatus) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="入场卷号">{{ coilDetail.enterCoilNo || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="当前卷号">{{ coilDetail.currentCoilNo || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="库区ID">{{ coilDetail.warehouseId || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="生产时长">{{ formatNumber(coilDetail.duration, 2) }} h</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="分摊能耗">{{ formatNumber(coilDetail.consumption, 2) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="分摊成本">¥ {{ formatNumber(coilDetail.totalCost, 2) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="开始时间" :span="2">{{ formatTime(coilDetail.startTime) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="完成时间" :span="2">{{ formatTime(coilDetail.endTime) }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-skeleton>
|
||||||
|
<span slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="coilDetailVisible = false">关 闭</el-button>
|
||||||
|
</span>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts';
|
||||||
import { listEnergyCoilDaily, listWarehouse } from '@/api/ems/energyAllocation'
|
import { listWarehouse } from '@/api/ems/energyAllocation';
|
||||||
|
import { listWarehouseProductionStats, listWarehouseProductionDetail, getWarehouseProductionCoilDetail } from '@/api/ems/warehouseProduction';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WarehouseProduction',
|
name: 'WarehouseProduction',
|
||||||
@@ -162,11 +195,14 @@ export default {
|
|||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
queryParams: {
|
queryParams: {
|
||||||
logicWarehouseId: '',
|
logicWarehouseId: undefined,
|
||||||
startDate: '',
|
startDate: undefined,
|
||||||
endDate: ''
|
endDate: undefined,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 15
|
||||||
},
|
},
|
||||||
warehouseList: [],
|
warehouseList: [],
|
||||||
|
total: 0,
|
||||||
statistics: {
|
statistics: {
|
||||||
coilCount: 0,
|
coilCount: 0,
|
||||||
totalDuration: 0,
|
totalDuration: 0,
|
||||||
@@ -176,7 +212,10 @@ export default {
|
|||||||
},
|
},
|
||||||
energyBreakdown: [],
|
energyBreakdown: [],
|
||||||
coilList: [],
|
coilList: [],
|
||||||
chartInstances: {}
|
chartInstances: {},
|
||||||
|
coilDetailVisible: false,
|
||||||
|
coilDetailLoading: false,
|
||||||
|
coilDetail: {}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -185,181 +224,47 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loadWarehouseList() {
|
loadWarehouseList() {
|
||||||
// 调用后端API获取库区列表
|
listWarehouse().then(res => {
|
||||||
listWarehouse().then(response => {
|
this.warehouseList = res.data || [];
|
||||||
this.warehouseList = response.data || [];
|
|
||||||
if (this.warehouseList.length === 0) {
|
|
||||||
this.$message.warning('暂无库区数据');
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
this.$message.error('加载库区列表失败');
|
this.warehouseList = [];
|
||||||
// 加载模拟数据作为备用
|
|
||||||
this.warehouseList = [
|
|
||||||
{ id: 1, name: '库区A' },
|
|
||||||
{ id: 2, name: '库区B' },
|
|
||||||
{ id: 3, name: '库区C' }
|
|
||||||
];
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handleQuery() {
|
handleQuery() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
// 调用后端API获取库区生产统计
|
Promise.all([
|
||||||
listEnergyCoilDaily(this.queryParams).then(response => {
|
listWarehouseProductionStats(this.queryParams),
|
||||||
const data = response.rows || [];
|
listWarehouseProductionDetail(this.queryParams)
|
||||||
this.processWarehouseData(data);
|
]).then(([statRes, detailRes]) => {
|
||||||
this.initCharts();
|
const stat = statRes.data || {};
|
||||||
|
this.statistics = {
|
||||||
|
coilCount: stat.coilCount || 0,
|
||||||
|
totalDuration: stat.totalDuration || 0,
|
||||||
|
totalConsumption: stat.totalConsumption || 0,
|
||||||
|
totalCost: stat.totalCost || 0,
|
||||||
|
unitCost: stat.unitCost || 0
|
||||||
|
};
|
||||||
|
this.energyBreakdown = (stat.energyBreakdownList || []).map(item => ({
|
||||||
|
...item,
|
||||||
|
percentage: item.percentage || 0,
|
||||||
|
averageUnitCost: item.averageUnitCost || 0
|
||||||
|
}));
|
||||||
|
this.coilList = detailRes.rows || [];
|
||||||
|
this.total = detailRes.total || 0;
|
||||||
|
this.$nextTick(() => this.initCharts());
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
this.$message.error('加载库区生产统计失败');
|
this.$message.error('加载数据失败');
|
||||||
// 加载模拟数据作为备用
|
|
||||||
this.loadMockData();
|
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
processWarehouseData(data) {
|
|
||||||
if (!data || data.length === 0) {
|
|
||||||
this.statistics = {
|
|
||||||
coilCount: 0,
|
|
||||||
totalDuration: 0,
|
|
||||||
totalConsumption: 0,
|
|
||||||
totalCost: 0,
|
|
||||||
unitCost: 0
|
|
||||||
};
|
|
||||||
this.energyBreakdown = [];
|
|
||||||
this.coilList = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 统计基本信息
|
|
||||||
const coilCount = new Set(data.map(d => d.coilId)).size;
|
|
||||||
const totalDuration = data.reduce((sum, d) => sum + (d.allocationBasisDays || 0), 0);
|
|
||||||
const totalCost = data.reduce((sum, d) => sum + (d.costAmount || 0), 0);
|
|
||||||
const totalConsumption = data.reduce((sum, d) => sum + (d.consumptionQty || 0), 0);
|
|
||||||
|
|
||||||
this.statistics = {
|
|
||||||
coilCount: coilCount,
|
|
||||||
totalDuration: totalDuration.toFixed(2),
|
|
||||||
totalConsumption: totalConsumption.toFixed(2),
|
|
||||||
totalCost: totalCost.toFixed(2),
|
|
||||||
unitCost: coilCount > 0 ? (totalCost / coilCount).toFixed(2) : 0
|
|
||||||
};
|
|
||||||
|
|
||||||
// 按能源类型聚合
|
|
||||||
const energyMap = {};
|
|
||||||
data.forEach(item => {
|
|
||||||
const key = item.energyTypeId;
|
|
||||||
if (!energyMap[key]) {
|
|
||||||
energyMap[key] = {
|
|
||||||
energyTypeId: item.energyTypeId,
|
|
||||||
energyTypeName: item.energyTypeName,
|
|
||||||
consumption: 0,
|
|
||||||
consumptionUnit: item.consumptionUnit || '单位',
|
|
||||||
cost: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
energyMap[key].consumption += (item.consumptionQty || 0);
|
|
||||||
energyMap[key].cost += (item.costAmount || 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.energyBreakdown = Object.values(energyMap).map(e => ({
|
|
||||||
...e,
|
|
||||||
percentage: totalCost > 0 ? ((e.cost / totalCost) * 100).toFixed(2) : 0,
|
|
||||||
averageUnitCost: e.consumption > 0 ? (e.cost / e.consumption).toFixed(4) : 0
|
|
||||||
}));
|
|
||||||
|
|
||||||
// 按钢卷聚合
|
|
||||||
const coilMap = {};
|
|
||||||
data.forEach(item => {
|
|
||||||
const key = item.coilId;
|
|
||||||
if (!coilMap[key]) {
|
|
||||||
coilMap[key] = {
|
|
||||||
coilId: item.coilId,
|
|
||||||
coilCode: item.currentCoilNo || item.enterCoilNo,
|
|
||||||
startTime: item.calcDate,
|
|
||||||
endTime: item.calcDate,
|
|
||||||
duration: item.allocationBasisDays || 0,
|
|
||||||
totalCost: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
coilMap[key].totalCost += (item.costAmount || 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.coilList = Object.values(coilMap);
|
|
||||||
},
|
|
||||||
loadMockData() {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.statistics = {
|
|
||||||
coilCount: 45,
|
|
||||||
totalDuration: 382.5,
|
|
||||||
totalConsumption: 38250,
|
|
||||||
totalCost: 56325.50,
|
|
||||||
unitCost: 1251.67
|
|
||||||
};
|
|
||||||
|
|
||||||
this.energyBreakdown = [
|
|
||||||
{
|
|
||||||
energyTypeId: 1,
|
|
||||||
energyTypeName: '电',
|
|
||||||
consumption: 32500,
|
|
||||||
consumptionUnit: 'kWh',
|
|
||||||
cost: 45875,
|
|
||||||
percentage: 81.45,
|
|
||||||
averageUnitCost: 1.4115
|
|
||||||
},
|
|
||||||
{
|
|
||||||
energyTypeId: 2,
|
|
||||||
energyTypeName: '水',
|
|
||||||
consumption: 4200,
|
|
||||||
consumptionUnit: '吨',
|
|
||||||
cost: 8400,
|
|
||||||
percentage: 14.91,
|
|
||||||
averageUnitCost: 2.0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
energyTypeId: 3,
|
|
||||||
energyTypeName: '气',
|
|
||||||
consumption: 1550,
|
|
||||||
consumptionUnit: '立方米',
|
|
||||||
cost: 2050.5,
|
|
||||||
percentage: 3.64,
|
|
||||||
averageUnitCost: 1.3229
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
this.coilList = [
|
|
||||||
{
|
|
||||||
coilCode: 'COIL-20231201-001',
|
|
||||||
startTime: '2023-12-01 08:00:00',
|
|
||||||
endTime: '2023-12-01 16:30:00',
|
|
||||||
duration: 8.5,
|
|
||||||
totalCost: 1250.50
|
|
||||||
},
|
|
||||||
{
|
|
||||||
coilCode: 'COIL-20231201-002',
|
|
||||||
startTime: '2023-12-01 17:00:00',
|
|
||||||
endTime: '2023-12-02 01:30:00',
|
|
||||||
duration: 8.5,
|
|
||||||
totalCost: 1248.75
|
|
||||||
},
|
|
||||||
{
|
|
||||||
coilCode: 'COIL-20231202-001',
|
|
||||||
startTime: '2023-12-02 08:00:00',
|
|
||||||
endTime: '2023-12-02 16:15:00',
|
|
||||||
duration: 8.25,
|
|
||||||
totalCost: 1205.30
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.initCharts();
|
|
||||||
});
|
|
||||||
}, 500);
|
|
||||||
},
|
|
||||||
resetQuery() {
|
resetQuery() {
|
||||||
this.queryParams = {
|
this.queryParams = {
|
||||||
logicWarehouseId: '',
|
logicWarehouseId: undefined,
|
||||||
startDate: '',
|
startDate: undefined,
|
||||||
endDate: ''
|
endDate: undefined,
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 15
|
||||||
};
|
};
|
||||||
this.handleQuery();
|
this.handleQuery();
|
||||||
},
|
},
|
||||||
@@ -368,94 +273,67 @@ export default {
|
|||||||
this.initConsumptionPieChart();
|
this.initConsumptionPieChart();
|
||||||
},
|
},
|
||||||
initCostPieChart() {
|
initCostPieChart() {
|
||||||
const chartDom = document.getElementById('energyCostPieChart');
|
const dom = document.getElementById('energyCostPieChart');
|
||||||
if (!chartDom) return;
|
if (!dom) return;
|
||||||
|
const ins = echarts.init(dom);
|
||||||
const instance = echarts.init(chartDom);
|
this.chartInstances.cost = ins;
|
||||||
this.chartInstances.cost = instance;
|
ins.setOption({
|
||||||
|
tooltip: { trigger: 'item', formatter: '{b}: ¥{c} ({d}%)' },
|
||||||
const option = {
|
legend: { orient: 'vertical', left: 'left' },
|
||||||
tooltip: {
|
|
||||||
trigger: 'item',
|
|
||||||
formatter: '{b}: ¥{c} ({d}%)'
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
orient: 'vertical',
|
|
||||||
left: 'left'
|
|
||||||
},
|
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '能源成本',
|
name: '能源成本',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: '50%',
|
radius: '50%',
|
||||||
data: this.energyBreakdown.map(item => ({
|
data: this.energyBreakdown.map(i => ({ value: i.cost, name: i.energyTypeName })),
|
||||||
value: item.cost,
|
emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0,0,0,0.5)' } }
|
||||||
name: item.energyTypeName
|
|
||||||
})),
|
|
||||||
emphasis: {
|
|
||||||
itemStyle: {
|
|
||||||
shadowBlur: 10,
|
|
||||||
shadowOffsetX: 0,
|
|
||||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
});
|
||||||
|
|
||||||
instance.setOption(option);
|
|
||||||
},
|
},
|
||||||
initConsumptionPieChart() {
|
initConsumptionPieChart() {
|
||||||
const chartDom = document.getElementById('energyConsumptionPieChart');
|
const dom = document.getElementById('energyConsumptionPieChart');
|
||||||
if (!chartDom) return;
|
if (!dom) return;
|
||||||
|
const ins = echarts.init(dom);
|
||||||
const instance = echarts.init(chartDom);
|
this.chartInstances.consumption = ins;
|
||||||
this.chartInstances.consumption = instance;
|
ins.setOption({
|
||||||
|
tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
|
||||||
const option = {
|
legend: { orient: 'vertical', left: 'left' },
|
||||||
tooltip: {
|
|
||||||
trigger: 'item',
|
|
||||||
formatter: '{b}: {c} ({d}%)'
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
orient: 'vertical',
|
|
||||||
left: 'left'
|
|
||||||
},
|
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '能源消耗',
|
name: '能源消耗',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: '50%',
|
radius: '50%',
|
||||||
data: this.energyBreakdown.map(item => ({
|
data: this.energyBreakdown.map(i => ({ value: i.consumption, name: i.energyTypeName })),
|
||||||
value: item.consumption,
|
emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0,0,0,0.5)' } }
|
||||||
name: item.energyTypeName
|
|
||||||
})),
|
|
||||||
emphasis: {
|
|
||||||
itemStyle: {
|
|
||||||
shadowBlur: 10,
|
|
||||||
shadowOffsetX: 0,
|
|
||||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
});
|
||||||
|
|
||||||
instance.setOption(option);
|
|
||||||
},
|
|
||||||
formatTime(time) {
|
|
||||||
if (!time) return '-';
|
|
||||||
return new Date(time).toLocaleString('zh-CN');
|
|
||||||
},
|
|
||||||
formatNumber(value, decimals = 2) {
|
|
||||||
if (value === null || value === undefined) return '0.00';
|
|
||||||
return parseFloat(value).toFixed(decimals);
|
|
||||||
},
|
},
|
||||||
viewCoilDetail(row) {
|
viewCoilDetail(row) {
|
||||||
this.$router.push({
|
if (!row || !row.coilId) {
|
||||||
path: '/ems/cost/coilCost',
|
this.$message.warning('未找到钢卷ID');
|
||||||
query: { coilId: row.coilId }
|
return;
|
||||||
|
}
|
||||||
|
this.coilDetailVisible = true;
|
||||||
|
this.coilDetailLoading = true;
|
||||||
|
getWarehouseProductionCoilDetail(row.coilId).then(res => {
|
||||||
|
this.coilDetail = res.data || {};
|
||||||
|
}).finally(() => {
|
||||||
|
this.coilDetailLoading = false;
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
formatNumber(value, decimals = 2) {
|
||||||
|
const num = Number(value) || 0;
|
||||||
|
return num.toFixed(decimals);
|
||||||
|
},
|
||||||
|
formatTime(value) {
|
||||||
|
if (!value) return '-';
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
formatActionStatus(val) {
|
||||||
|
const map = { 0: '待上料', 1: '在产', 2: '完成' };
|
||||||
|
return map[val] || '-';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
|
|||||||
@@ -191,7 +191,7 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 库区详情弹窗 -->
|
<!-- 库区详情弹窗 -->
|
||||||
<el-dialog :title="`库区详情 - ${selectedWarehouse ? selectedWarehouse.warehouseName : ''}`" :visible.sync="warehouseDetailVisible" width="800px" append-to-body>
|
<el-dialog :title="`库区详情 - ${selectedWarehouse ? selectedWarehouse.warehouseName : ''}`" :visible.sync="warehouseDetailVisible" width="1000px" append-to-body>
|
||||||
<div v-if="selectedWarehouse" class="warehouse-detail">
|
<div v-if="selectedWarehouse" class="warehouse-detail">
|
||||||
<!-- 库区基本信息 -->
|
<!-- 库区基本信息 -->
|
||||||
<div class="detail-section">
|
<div class="detail-section">
|
||||||
@@ -212,12 +212,66 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 可用设备卡片列表 -->
|
||||||
|
<div class="detail-section" v-if="availableMeters.length > 0">
|
||||||
|
<h3>可绑定设备 ({{ availableMeters.length }})</h3>
|
||||||
|
<div class="meter-cards-grid">
|
||||||
|
<div
|
||||||
|
v-for="meter in paginatedMeters"
|
||||||
|
:key="meter.meterId"
|
||||||
|
class="meter-card"
|
||||||
|
@click="bindMeterToWarehouse(meter)"
|
||||||
|
>
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="meter-code">{{ meter.meterCode }}</div>
|
||||||
|
<el-tag type="success" size="small">{{ getEnergyName(meter.energyTypeId) }}</el-tag>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="info-row">
|
||||||
|
<span class="label">型号:</span>
|
||||||
|
<span class="value">{{ meter.model || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<span class="label">制造商:</span>
|
||||||
|
<span class="value">{{ meter.manufacturer || '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<span class="label">安装日期:</span>
|
||||||
|
<span class="value">{{ meter.installDate ? meter.installDate.substring(0, 10) : '-' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="info-row">
|
||||||
|
<span class="label">状态:</span>
|
||||||
|
<span class="value">{{ meter.status === 1 ? '正常' : '异常' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer">
|
||||||
|
<el-button type="primary" size="small" @click.stop="bindMeterToWarehouse(meter)">绑定</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-container" v-if="meterTotalPages > 1">
|
||||||
|
<el-pagination
|
||||||
|
:current-page.sync="meterPageNum"
|
||||||
|
:page-size="meterPageSize"
|
||||||
|
:total="availableMeters.length"
|
||||||
|
layout="prev, pager, next"
|
||||||
|
@current-change="meterPageNum = $event"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 无可用设备提示 -->
|
||||||
|
<div class="detail-section" v-else>
|
||||||
|
<el-empty description="暂无可绑定设备"></el-empty>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 绑定设备列表 -->
|
<!-- 绑定设备列表 -->
|
||||||
<div class="detail-section">
|
<div class="detail-section">
|
||||||
<div class="section-header">
|
<div class="section-header">
|
||||||
<h3>绑定设备 ({{ getWarehouseDevices(selectedWarehouse.warehouseId).length }})</h3>
|
<h3>绑定设备 ({{ getWarehouseDevices(selectedWarehouse.warehouseId).length }})</h3>
|
||||||
<div class="section-actions">
|
<div class="section-actions">
|
||||||
<el-button type="primary" size="small" @click="handleBindDevice(selectedWarehouse)">添加设备</el-button>
|
|
||||||
<el-button
|
<el-button
|
||||||
v-if="selectedBindings.length > 0"
|
v-if="selectedBindings.length > 0"
|
||||||
type="danger"
|
type="danger"
|
||||||
@@ -245,24 +299,14 @@
|
|||||||
@selection-change="selectedBindings = $event"
|
@selection-change="selectedBindings = $event"
|
||||||
>
|
>
|
||||||
<el-table-column type="selection" width="50" />
|
<el-table-column type="selection" width="50" />
|
||||||
<el-table-column prop="meterCode" label="设备编号" width="150" />
|
<el-table-column prop="meterCode" label="设备编号" />
|
||||||
<el-table-column label="能源类型" width="120">
|
<el-table-column label="能源类型">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
{{ getEnergyName(scope.row.energyTypeId) }}
|
{{ getEnergyName(scope.row.energyTypeId) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="model" label="型号" width="120" />
|
<el-table-column prop="model" label="型号" />
|
||||||
<el-table-column prop="manufacturer" label="制造商" width="120" />
|
<el-table-column prop="manufacturer" label="制造商" />
|
||||||
<el-table-column label="分摊模式" width="120">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
{{ getModeLabel(scope.row.allocationMode) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="权重" width="80">
|
|
||||||
<template slot-scope="scope">
|
|
||||||
{{ scope.row.weightRatio || '-' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" width="100">
|
<el-table-column label="操作" width="100">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button type="text" size="small" @click="handleEditBinding(scope.row)">编辑</el-button>
|
<el-button type="text" size="small" @click="handleEditBinding(scope.row)">编辑</el-button>
|
||||||
@@ -274,43 +318,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 绑定设备弹窗 -->
|
|
||||||
<el-dialog title="绑定设备" :visible.sync="bindDeviceDialogVisible" width="600px" append-to-body>
|
|
||||||
<div v-if="currentWarehouse" class="bind-device-form">
|
|
||||||
<el-form :model="bindForm" :rules="bindRules" ref="bindForm" label-width="100px">
|
|
||||||
<el-form-item label="选择设备" prop="meterId">
|
|
||||||
<el-select v-model="bindForm.meterId" placeholder="请选择设备" clearable>
|
|
||||||
<el-option
|
|
||||||
v-for="meter in availableMeters"
|
|
||||||
:key="meter.meterId"
|
|
||||||
:label="`${meter.meterCode} (${getEnergyName(meter.energyTypeId)})`"
|
|
||||||
:value="meter.meterId"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="分摊模式" prop="allocationMode">
|
|
||||||
<el-select v-model="bindForm.allocationMode">
|
|
||||||
<el-option label="重量 × 时长" value="weight_time" />
|
|
||||||
<el-option label="重量" value="weight" />
|
|
||||||
<el-option label="数量" value="count" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="权重系数" prop="weightRatio">
|
|
||||||
<el-input-number v-model="bindForm.weightRatio" :min="0" :step="0.1" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="仅待操作">
|
|
||||||
<el-switch v-model="bindForm.requirePendingAction" :active-value="1" :inactive-value="0" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
|
||||||
<div slot="footer" class="dialog-footer">
|
|
||||||
<el-button :loading="bindLoading" type="primary" @click="submitBindDevice">确 定</el-button>
|
|
||||||
<el-button @click="bindDeviceDialogVisible = false">取 消</el-button>
|
|
||||||
</div>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- 编辑绑定弹窗 -->
|
<!-- 编辑绑定弹窗 -->
|
||||||
<el-dialog title="编辑绑定参数" :visible.sync="editBindingDialogVisible" width="500px" append-to-body>
|
<el-dialog title="编辑绑定参数" :visible.sync="editBindingDialogVisible" width="500px" append-to-body">
|
||||||
<div v-if="editingBinding" class="edit-binding-form">
|
<div v-if="editingBinding" class="edit-binding-form">
|
||||||
<div class="binding-info">
|
<div class="binding-info">
|
||||||
<div class="info-row">
|
<div class="info-row">
|
||||||
@@ -323,25 +333,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-divider />
|
<el-divider />
|
||||||
<el-form :model="editBindingForm" :rules="editBindingRules" ref="editBindingForm" label-width="100px">
|
<div class="info-text">该绑定关系已保存,无需编辑参数。</div>
|
||||||
<el-form-item label="分摊模式" prop="allocationMode">
|
|
||||||
<el-select v-model="editBindingForm.allocationMode">
|
|
||||||
<el-option label="重量 × 时长" value="weight_time" />
|
|
||||||
<el-option label="重量" value="weight" />
|
|
||||||
<el-option label="数量" value="count" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="权重系数" prop="weightRatio">
|
|
||||||
<el-input-number v-model="editBindingForm.weightRatio" :min="0" :step="0.1" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="仅待操作">
|
|
||||||
<el-switch v-model="editBindingForm.requirePendingAction" :active-value="1" :inactive-value="0" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</div>
|
</div>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button :loading="bindLoading" type="primary" @click="submitEditBinding">确 定</el-button>
|
<el-button @click="editBindingDialogVisible = false">关 闭</el-button>
|
||||||
<el-button @click="editBindingDialogVisible = false">取 消</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
@@ -352,6 +347,7 @@ import { listMeter, getMeter, delMeter, addMeter, updateMeter } from "@/api/ems/
|
|||||||
import { listEnergyType } from "@/api/ems/energyType";
|
import { listEnergyType } from "@/api/ems/energyType";
|
||||||
import { listWarehouse } from '@/api/wms/warehouse'
|
import { listWarehouse } from '@/api/wms/warehouse'
|
||||||
import { fetchEnergyLinkMatrix, addEnergyLink, updateEnergyLink, deleteEnergyLink, batchDeleteEnergyLinks, exportEnergyLinks, getStatistics } from '@/api/ems/energyLink'
|
import { fetchEnergyLinkMatrix, addEnergyLink, updateEnergyLink, deleteEnergyLink, batchDeleteEnergyLinks, exportEnergyLinks, getStatistics } from '@/api/ems/energyLink'
|
||||||
|
import { getLatestMeterReadTime } from '@/api/ems/energyConsumption'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MeterNew",
|
name: "MeterNew",
|
||||||
@@ -405,50 +401,38 @@ export default {
|
|||||||
warehouseDetailVisible: false,
|
warehouseDetailVisible: false,
|
||||||
selectedWarehouse: null,
|
selectedWarehouse: null,
|
||||||
|
|
||||||
// 绑定设备
|
// 设备卡片分页
|
||||||
bindDeviceDialogVisible: false,
|
meterPageNum: 1,
|
||||||
currentWarehouse: null,
|
meterPageSize: 10,
|
||||||
bindForm: {
|
|
||||||
meterId: undefined,
|
|
||||||
allocationMode: 'weight_time',
|
|
||||||
weightRatio: 1,
|
|
||||||
requirePendingAction: 1
|
|
||||||
},
|
|
||||||
bindRules: {
|
|
||||||
meterId: [{ required: true, message: '请选择设备', trigger: 'change' }],
|
|
||||||
allocationMode: [{ required: true, message: '请选择分摊模式', trigger: 'change' }],
|
|
||||||
weightRatio: [{ required: true, message: '请输入权重系数', trigger: 'blur' }]
|
|
||||||
},
|
|
||||||
|
|
||||||
// 编辑绑定
|
// 编辑绑定
|
||||||
editBindingDialogVisible: false,
|
editBindingDialogVisible: false,
|
||||||
editingBinding: null,
|
editingBinding: null,
|
||||||
editBindingForm: {
|
editBindingForm: {},
|
||||||
allocationMode: 'weight_time',
|
editBindingRules: {},
|
||||||
weightRatio: 1,
|
|
||||||
requirePendingAction: 1
|
|
||||||
},
|
|
||||||
editBindingRules: {
|
|
||||||
allocationMode: [{ required: true, message: '请选择分摊模式', trigger: 'change' }],
|
|
||||||
weightRatio: [{ required: true, message: '请输入权重系数', trigger: 'blur' }]
|
|
||||||
},
|
|
||||||
|
|
||||||
// 库区搜索
|
// 库区搜索
|
||||||
warehouseSearchKeyword: '',
|
warehouseSearchKeyword: '',
|
||||||
|
|
||||||
// 批量操作
|
// 批量操作
|
||||||
selectedBindings: [],
|
selectedBindings: [],
|
||||||
batchDeleteLoading: false
|
batchDeleteLoading: false,
|
||||||
|
|
||||||
|
// 最近抄表时间
|
||||||
|
latestMeterReadTime: {
|
||||||
|
latestMeterReadStartTime: null,
|
||||||
|
latestMeterReadEndTime: null
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
availableMeters() {
|
availableMeters() {
|
||||||
// 返回未绑定到当前库区的设备
|
// 返回未绑定到当前库区的设备
|
||||||
if (!this.currentWarehouse) return [];
|
if (!this.selectedWarehouse) return [];
|
||||||
const boundMeterIds = new Set();
|
const boundMeterIds = new Set();
|
||||||
(this.matrixData || []).forEach(wh => {
|
(this.matrixData || []).forEach(wh => {
|
||||||
if (wh.warehouseId === this.currentWarehouse.warehouseId) {
|
if (wh.warehouseId === this.selectedWarehouse.warehouseId) {
|
||||||
(wh.links || []).forEach(link => {
|
(wh.links || []).forEach(link => {
|
||||||
if (link.meterId) boundMeterIds.add(link.meterId);
|
if (link.meterId) boundMeterIds.add(link.meterId);
|
||||||
});
|
});
|
||||||
@@ -457,6 +441,18 @@ export default {
|
|||||||
return this.meterList.filter(m => !boundMeterIds.has(m.meterId));
|
return this.meterList.filter(m => !boundMeterIds.has(m.meterId));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 分页后的设备列表
|
||||||
|
paginatedMeters() {
|
||||||
|
const start = (this.meterPageNum - 1) * this.meterPageSize;
|
||||||
|
const end = start + this.meterPageSize;
|
||||||
|
return this.availableMeters.slice(start, end);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 设备总页数
|
||||||
|
meterTotalPages() {
|
||||||
|
return Math.ceil(this.availableMeters.length / this.meterPageSize);
|
||||||
|
},
|
||||||
|
|
||||||
filteredWarehouses() {
|
filteredWarehouses() {
|
||||||
// 库区搜索过滤
|
// 库区搜索过滤
|
||||||
if (!this.warehouseSearchKeyword.trim()) {
|
if (!this.warehouseSearchKeyword.trim()) {
|
||||||
@@ -476,6 +472,7 @@ export default {
|
|||||||
this.loadWarehouseList();
|
this.loadWarehouseList();
|
||||||
this.loadMatrix();
|
this.loadMatrix();
|
||||||
this.loadStatistics();
|
this.loadStatistics();
|
||||||
|
this.loadLatestMeterReadTime();
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@@ -570,14 +567,6 @@ export default {
|
|||||||
return energy ? energy.name : '-';
|
return energy ? energy.name : '-';
|
||||||
},
|
},
|
||||||
|
|
||||||
getModeLabel(mode) {
|
|
||||||
const modeDict = {
|
|
||||||
weight_time: '重量 × 时长',
|
|
||||||
weight: '按重量',
|
|
||||||
count: '按数量'
|
|
||||||
};
|
|
||||||
return modeDict[mode] || mode;
|
|
||||||
},
|
|
||||||
|
|
||||||
handleQuery() {
|
handleQuery() {
|
||||||
this.queryParams.pageNum = 1;
|
this.queryParams.pageNum = 1;
|
||||||
@@ -628,78 +617,35 @@ export default {
|
|||||||
this.warehouseDetailVisible = true;
|
this.warehouseDetailVisible = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
handleBindDevice(warehouse) {
|
bindMeterToWarehouse(meter) {
|
||||||
this.currentWarehouse = warehouse;
|
if (!this.selectedWarehouse) {
|
||||||
this.bindForm = {
|
this.$message.error('请先选择库区');
|
||||||
meterId: undefined,
|
return;
|
||||||
allocationMode: 'weight_time',
|
}
|
||||||
weightRatio: 1,
|
const payload = {
|
||||||
requirePendingAction: 1
|
warehouseId: this.selectedWarehouse.warehouseId,
|
||||||
|
meterId: meter.meterId,
|
||||||
|
energyTypeId: meter.energyTypeId
|
||||||
};
|
};
|
||||||
this.bindDeviceDialogVisible = true;
|
this.bindLoading = true;
|
||||||
},
|
addEnergyLink(payload)
|
||||||
|
.then(() => {
|
||||||
submitBindDevice() {
|
this.$modal.msgSuccess('绑定成功');
|
||||||
this.$refs.bindForm.validate(valid => {
|
this.loadMatrix();
|
||||||
if (!valid) return;
|
})
|
||||||
const { meterId, allocationMode, weightRatio, requirePendingAction } = this.bindForm;
|
.finally(() => {
|
||||||
const meter = this.meterList.find(m => m.meterId === meterId);
|
this.bindLoading = false;
|
||||||
if (!meter) {
|
});
|
||||||
this.$message.error('设备不存在');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const payload = {
|
|
||||||
warehouseId: this.currentWarehouse.warehouseId,
|
|
||||||
meterId: meter.meterId,
|
|
||||||
energyTypeId: meter.energyTypeId,
|
|
||||||
allocationMode,
|
|
||||||
weightRatio,
|
|
||||||
requirePendingAction
|
|
||||||
};
|
|
||||||
this.bindLoading = true;
|
|
||||||
addEnergyLink(payload)
|
|
||||||
.then(() => {
|
|
||||||
this.$modal.msgSuccess('绑定成功');
|
|
||||||
this.bindDeviceDialogVisible = false;
|
|
||||||
this.loadMatrix();
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.bindLoading = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handleEditBinding(binding) {
|
handleEditBinding(binding) {
|
||||||
this.editingBinding = binding;
|
this.editingBinding = binding;
|
||||||
this.editBindingForm = {
|
this.editBindingForm = {};
|
||||||
allocationMode: binding.allocationMode || 'weight_time',
|
|
||||||
weightRatio: binding.weightRatio || 1,
|
|
||||||
requirePendingAction: binding.requirePendingAction || 1
|
|
||||||
};
|
|
||||||
this.editBindingDialogVisible = true;
|
this.editBindingDialogVisible = true;
|
||||||
},
|
},
|
||||||
|
|
||||||
submitEditBinding() {
|
submitEditBinding() {
|
||||||
this.$refs.editBindingForm.validate(valid => {
|
this.editBindingDialogVisible = false;
|
||||||
if (!valid) return;
|
|
||||||
const { allocationMode, weightRatio, requirePendingAction } = this.editBindingForm;
|
|
||||||
const payload = {
|
|
||||||
linkId: this.editingBinding.linkId,
|
|
||||||
allocationMode,
|
|
||||||
weightRatio,
|
|
||||||
requirePendingAction
|
|
||||||
};
|
|
||||||
this.bindLoading = true;
|
|
||||||
updateEnergyLink(payload)
|
|
||||||
.then(() => {
|
|
||||||
this.$modal.msgSuccess('更新成功');
|
|
||||||
this.editBindingDialogVisible = false;
|
|
||||||
this.loadMatrix();
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
this.bindLoading = false;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
handleDeleteBinding(binding) {
|
handleDeleteBinding(binding) {
|
||||||
@@ -795,6 +741,20 @@ export default {
|
|||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.$message.error('绑定失败');
|
this.$message.error('绑定失败');
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
loadLatestMeterReadTime() {
|
||||||
|
getLatestMeterReadTime().then(response => {
|
||||||
|
if (response && response.data) {
|
||||||
|
this.latestMeterReadTime = {
|
||||||
|
latestMeterReadStartTime: response.data.startTime,
|
||||||
|
latestMeterReadEndTime: response.data.endTime
|
||||||
|
};
|
||||||
|
console.log('最近一次抄表时间范围:', this.latestMeterReadTime);
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
console.warn('获取最近一次抄表时间失败');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1150,6 +1110,85 @@ export default {
|
|||||||
padding: 20px 0;
|
padding: 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.meter-cards-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.meter-card {
|
||||||
|
border: 1px solid #e4e7eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
background: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #409eff;
|
||||||
|
box-shadow: 0 2px 12px rgba(64, 158, 255, 0.2);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
|
||||||
|
.meter-code {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #303133;
|
||||||
|
flex: 1;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
flex: 1;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.info-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 6px 0;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
color: #909399;
|
||||||
|
min-width: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
color: #303133;
|
||||||
|
text-align: right;
|
||||||
|
flex: 1;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px 0;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
.fade-enter-active, .fade-leave-active {
|
.fade-enter-active, .fade-leave-active {
|
||||||
transition: opacity 0.3s;
|
transition: opacity 0.3s;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user