Compare commits
2 Commits
0e808964c9
...
test.X
| Author | SHA1 | Date | |
|---|---|---|---|
| 75a2623d8b | |||
| 5d046be15b |
@@ -11,7 +11,7 @@ import org.springframework.boot.context.metrics.buffering.BufferingApplicationSt
|
||||
*/
|
||||
|
||||
//@SpringBootApplication
|
||||
@SpringBootApplication
|
||||
@SpringBootApplication(exclude = org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.class)
|
||||
public class KLPApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -130,6 +130,10 @@ spring:
|
||||
# 多久检查一次连接的活性
|
||||
keepaliveTime: 30000
|
||||
|
||||
flyway:
|
||||
baseline-on-migrate: true # 第一次运行时建立记录,不执行历史脚本
|
||||
clean-disabled: true # 禁止清空库
|
||||
|
||||
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
||||
spring:
|
||||
redis:
|
||||
|
||||
@@ -124,6 +124,10 @@ spring:
|
||||
# 多久检查一次连接的活性
|
||||
keepaliveTime: 30000
|
||||
|
||||
flyway:
|
||||
baseline-on-migrate: true # 第一次运行时建立记录,不执行历史脚本
|
||||
clean-disabled: true # 禁止清空库
|
||||
|
||||
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
||||
spring:
|
||||
redis:
|
||||
|
||||
@@ -101,6 +101,11 @@ spring:
|
||||
deserialization:
|
||||
# 允许对象忽略json中不存在的属性
|
||||
fail_on_unknown_properties: false
|
||||
# 实时更新数据库结构
|
||||
flyway:
|
||||
enabled: true
|
||||
locations: classpath:db/migration
|
||||
table: flyway_schema_history
|
||||
|
||||
# Sa-Token配置
|
||||
sa-token:
|
||||
@@ -337,9 +342,3 @@ stamp:
|
||||
base-url: http://python-stamp-service.example.com # 替换为实际地址
|
||||
api-key: changeme # 替换为实际鉴权信息
|
||||
timeout-ms: 5000 # 可按需调整
|
||||
|
||||
# OEE配置
|
||||
oee:
|
||||
acid:
|
||||
# 酸轧入场卷创建人(wms_material_coil.create_by)
|
||||
coil-create-by: suanzhakuguan
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS aps_quick_sheet (
|
||||
quick_sheet_id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
plan_date DATE NOT NULL COMMENT '计划日期',
|
||||
line_id BIGINT NULL COMMENT '产线ID',
|
||||
line_name VARCHAR(120) NULL COMMENT '产线名称',
|
||||
plan_code VARCHAR(60) NOT NULL COMMENT '计划号',
|
||||
order_code VARCHAR(80) NULL COMMENT '订单号',
|
||||
customer_name VARCHAR(120) NULL COMMENT '客户',
|
||||
salesman VARCHAR(60) NULL COMMENT '业务员',
|
||||
product_name VARCHAR(120) NULL COMMENT '产品',
|
||||
raw_material_id VARCHAR(64) NULL COMMENT '原料钢卷',
|
||||
raw_coil_nos VARCHAR(255) NULL COMMENT '原料卷号',
|
||||
raw_location VARCHAR(120) NULL COMMENT '钢卷位置',
|
||||
raw_packaging VARCHAR(120) NULL COMMENT '包装要求',
|
||||
raw_edge_req VARCHAR(120) NULL COMMENT '切边要求',
|
||||
raw_coating_type VARCHAR(120) NULL COMMENT '镀层种类',
|
||||
raw_net_weight DECIMAL(18, 3) NULL COMMENT '原料净重',
|
||||
plan_qty DECIMAL(18, 3) NULL COMMENT '计划数量',
|
||||
start_time DATETIME NULL COMMENT '开始时间',
|
||||
end_time DATETIME NULL COMMENT '结束时间',
|
||||
del_flag TINYINT DEFAULT 0 COMMENT '删除标记(0正常 1删除)',
|
||||
create_by VARCHAR(64) NULL,
|
||||
update_by VARCHAR(64) NULL,
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_aps_quick_sheet_plan_date (plan_date),
|
||||
INDEX idx_aps_quick_sheet_line_id (line_id)
|
||||
) COMMENT = '快速排产表(Excel样式)';
|
||||
@@ -1,47 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS wms_furnace (
|
||||
furnace_id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
furnace_code VARCHAR(50) NOT NULL COMMENT '炉编号',
|
||||
furnace_name VARCHAR(100) NOT NULL COMMENT '名称',
|
||||
busy_flag TINYINT DEFAULT 0 COMMENT '是否忙碌(0否1是)',
|
||||
status TINYINT DEFAULT 1 COMMENT '状态(0停用1启用)',
|
||||
remark VARCHAR(500) NULL COMMENT '备注',
|
||||
del_flag TINYINT DEFAULT 0 COMMENT '删除标记(0正常 1删除)',
|
||||
create_by VARCHAR(64) NULL,
|
||||
update_by VARCHAR(64) NULL,
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_wms_furnace_code (furnace_code)
|
||||
) COMMENT = '退火炉信息表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wms_furnace_plan (
|
||||
plan_id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
plan_no VARCHAR(60) NOT NULL COMMENT '计划号',
|
||||
plan_start_time DATETIME NULL COMMENT '计划开始时间',
|
||||
actual_start_time DATETIME NULL COMMENT '实际开始时间',
|
||||
end_time DATETIME NULL COMMENT '结束时间',
|
||||
target_furnace_id BIGINT NOT NULL COMMENT '目标炉子ID',
|
||||
status TINYINT DEFAULT 0 COMMENT '计划状态(0未开始 1进行中 2已完成)',
|
||||
remark VARCHAR(500) NULL COMMENT '备注',
|
||||
del_flag TINYINT DEFAULT 0 COMMENT '删除标记(0正常 1删除)',
|
||||
create_by VARCHAR(64) NULL,
|
||||
update_by VARCHAR(64) NULL,
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_wms_furnace_plan_no (plan_no),
|
||||
INDEX idx_wms_furnace_plan_furnace (target_furnace_id),
|
||||
INDEX idx_wms_furnace_plan_status (status)
|
||||
) COMMENT = '退火计划表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS wms_furnace_plan_coil (
|
||||
plan_coil_id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
plan_id BIGINT NOT NULL COMMENT '计划ID',
|
||||
coil_id BIGINT NOT NULL COMMENT '钢卷ID',
|
||||
del_flag TINYINT DEFAULT 0 COMMENT '删除标记(0正常 1删除)',
|
||||
create_by VARCHAR(64) NULL,
|
||||
update_by VARCHAR(64) NULL,
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_wms_furnace_plan_coil (plan_id, coil_id),
|
||||
INDEX idx_wms_furnace_plan_coil_plan (plan_id),
|
||||
INDEX idx_wms_furnace_plan_coil_coil (coil_id)
|
||||
) COMMENT = '退火计划钢卷关系表';
|
||||
@@ -1,55 +0,0 @@
|
||||
package com.klp.aps.controller;
|
||||
|
||||
import com.klp.aps.domain.dto.ApsQuickSheetQueryReq;
|
||||
import com.klp.aps.domain.dto.ApsQuickSheetSaveReq;
|
||||
import com.klp.aps.domain.vo.ApsQuickSheetRowVo;
|
||||
import com.klp.aps.service.ApsQuickSheetService;
|
||||
import com.klp.common.core.controller.BaseController;
|
||||
import com.klp.common.core.domain.R;
|
||||
import com.klp.common.helper.LoginHelper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/aps/quick-sheet")
|
||||
public class ApsQuickSheetController extends BaseController {
|
||||
|
||||
private final ApsQuickSheetService quickSheetService;
|
||||
|
||||
@GetMapping("/list")
|
||||
public R<List<ApsQuickSheetRowVo>> list(@Validated ApsQuickSheetQueryReq req) {
|
||||
return R.ok(quickSheetService.queryList(req));
|
||||
}
|
||||
|
||||
@GetMapping("/preset")
|
||||
public R<List<ApsQuickSheetRowVo>> preset(@RequestParam(value = "lineId", required = false) Long lineId) {
|
||||
String salesman = LoginHelper.getNickName();
|
||||
return R.ok(quickSheetService.buildPresetRows(lineId, salesman));
|
||||
}
|
||||
|
||||
@PostMapping("/save")
|
||||
public R<Void> save(@Validated @RequestBody ApsQuickSheetSaveReq req) {
|
||||
quickSheetService.saveRows(req, getUsername());
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/export")
|
||||
public void export(@Validated ApsQuickSheetQueryReq req, javax.servlet.http.HttpServletResponse response) {
|
||||
quickSheetService.exportExcel(req, response);
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
public R<Void> delete(@RequestParam("quickSheetId") Long quickSheetId) {
|
||||
quickSheetService.deleteById(quickSheetId, getUsername());
|
||||
return R.ok();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.klp.aps.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Data
|
||||
public class ApsQuickSheetQueryReq {
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate startDate;
|
||||
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate endDate;
|
||||
|
||||
private Long lineId;
|
||||
|
||||
private String customerName;
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package com.klp.aps.domain.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ApsQuickSheetSaveReq {
|
||||
|
||||
@NotEmpty(message = "rows 不能为空")
|
||||
private List<Row> rows;
|
||||
|
||||
@Data
|
||||
public static class Row {
|
||||
private Long quickSheetId;
|
||||
private Long lineId;
|
||||
private String lineName;
|
||||
private String planCode;
|
||||
private String planType;
|
||||
private String scheduler;
|
||||
private String remark;
|
||||
|
||||
private String bizSeqNo;
|
||||
private String orderCode;
|
||||
private String contractCode;
|
||||
private String customerName;
|
||||
private String salesman;
|
||||
|
||||
private String productName;
|
||||
private String productMaterial;
|
||||
private String coatingG;
|
||||
private String productWidth;
|
||||
private String rollingThick;
|
||||
private String markCoatThick;
|
||||
private String tonSteelLengthRange;
|
||||
private String planQty;
|
||||
private String planWeight;
|
||||
private String surfaceTreatment;
|
||||
private String widthReq;
|
||||
private String usageReq;
|
||||
private String postProcess;
|
||||
private String nextProcess;
|
||||
private String sampleReq;
|
||||
|
||||
private String rawManufacturer;
|
||||
private String rawMaterial;
|
||||
private String rawThick;
|
||||
private String rawWidth;
|
||||
private String rawMaterialId;
|
||||
private String rawCoilNos;
|
||||
private String rawLocation;
|
||||
private String rawPackaging;
|
||||
private String rawEdgeReq;
|
||||
private String rawCoatingType;
|
||||
private String rawNetWeight;
|
||||
private String startTime;
|
||||
private String endTime;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package com.klp.aps.domain.entity;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class ApsQuickSheetEntity {
|
||||
private Long quickSheetId;
|
||||
private LocalDate planDate;
|
||||
private Long lineId;
|
||||
private String lineName;
|
||||
private String planCode;
|
||||
private String planType;
|
||||
private String scheduler;
|
||||
private String remark;
|
||||
|
||||
private String bizSeqNo;
|
||||
private String orderCode;
|
||||
private String contractCode;
|
||||
private String customerName;
|
||||
private String salesman;
|
||||
|
||||
private String productName;
|
||||
private String productMaterial;
|
||||
private BigDecimal coatingG;
|
||||
private BigDecimal productWidth;
|
||||
private BigDecimal rollingThick;
|
||||
private BigDecimal markCoatThick;
|
||||
private String tonSteelLengthRange;
|
||||
private BigDecimal planQty;
|
||||
private BigDecimal planWeight;
|
||||
private String surfaceTreatment;
|
||||
private String widthReq;
|
||||
private String usageReq;
|
||||
private String postProcess;
|
||||
private String nextProcess;
|
||||
private String sampleReq;
|
||||
|
||||
private String rawManufacturer;
|
||||
private String rawMaterial;
|
||||
private BigDecimal rawThick;
|
||||
private BigDecimal rawWidth;
|
||||
private String rawMaterialId;
|
||||
private String rawCoilNos;
|
||||
private String rawLocation;
|
||||
private String rawPackaging;
|
||||
private String rawEdgeReq;
|
||||
private String rawCoatingType;
|
||||
private BigDecimal rawNetWeight;
|
||||
private LocalDateTime startTime;
|
||||
private LocalDateTime endTime;
|
||||
private String createBy;
|
||||
private String updateBy;
|
||||
private LocalDateTime createTime;
|
||||
private LocalDateTime updateTime;
|
||||
private Integer delFlag;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package com.klp.aps.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class ApsQuickSheetRowVo {
|
||||
private Long quickSheetId;
|
||||
private java.time.LocalDate planDate;
|
||||
private Long lineId;
|
||||
private String lineName;
|
||||
private String planCode;
|
||||
private String planType;
|
||||
private String scheduler;
|
||||
private String remark;
|
||||
|
||||
private String bizSeqNo;
|
||||
private String orderCode;
|
||||
private String contractCode;
|
||||
private String customerName;
|
||||
private String salesman;
|
||||
|
||||
private String productName;
|
||||
private String productMaterial;
|
||||
private BigDecimal coatingG;
|
||||
private BigDecimal productWidth;
|
||||
private BigDecimal rollingThick;
|
||||
private BigDecimal markCoatThick;
|
||||
private String tonSteelLengthRange;
|
||||
private BigDecimal planQty;
|
||||
private BigDecimal planWeight;
|
||||
private String surfaceTreatment;
|
||||
private String widthReq;
|
||||
private String usageReq;
|
||||
private String postProcess;
|
||||
private String nextProcess;
|
||||
private String sampleReq;
|
||||
|
||||
private String rawManufacturer;
|
||||
private String rawMaterial;
|
||||
private BigDecimal rawThick;
|
||||
private BigDecimal rawWidth;
|
||||
private String rawMaterialId;
|
||||
private String rawCoilNos;
|
||||
private String rawLocation;
|
||||
private String rawPackaging;
|
||||
private String rawEdgeReq;
|
||||
private String rawCoatingType;
|
||||
private BigDecimal rawNetWeight;
|
||||
private LocalDateTime startTime;
|
||||
private LocalDateTime endTime;
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
package com.klp.aps.mapper;
|
||||
|
||||
import com.klp.aps.domain.dto.ApsQuickSheetQueryReq;
|
||||
import com.klp.aps.domain.vo.ApsQuickSheetRowVo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
public interface ApsQuickSheetMapper {
|
||||
|
||||
List<ApsQuickSheetRowVo> selectList(ApsQuickSheetQueryReq req);
|
||||
|
||||
int countToday(@Param("planDate") LocalDate planDate);
|
||||
|
||||
Long selectIdByPlanCode(@Param("planCode") String planCode);
|
||||
|
||||
int insertRow(@Param("lineId") Long lineId,
|
||||
@Param("lineName") String lineName,
|
||||
@Param("planDate") LocalDate planDate,
|
||||
@Param("planCode") String planCode,
|
||||
@Param("planType") String planType,
|
||||
@Param("scheduler") String scheduler,
|
||||
@Param("remark") String remark,
|
||||
@Param("bizSeqNo") String bizSeqNo,
|
||||
@Param("orderCode") String orderCode,
|
||||
@Param("contractCode") String contractCode,
|
||||
@Param("customerName") String customerName,
|
||||
@Param("salesman") String salesman,
|
||||
@Param("productName") String productName,
|
||||
@Param("productMaterial") String productMaterial,
|
||||
@Param("coatingG") BigDecimal coatingG,
|
||||
@Param("productWidth") BigDecimal productWidth,
|
||||
@Param("rollingThick") BigDecimal rollingThick,
|
||||
@Param("markCoatThick") BigDecimal markCoatThick,
|
||||
@Param("tonSteelLengthRange") String tonSteelLengthRange,
|
||||
@Param("planQty") BigDecimal planQty,
|
||||
@Param("planWeight") BigDecimal planWeight,
|
||||
@Param("surfaceTreatment") String surfaceTreatment,
|
||||
@Param("widthReq") String widthReq,
|
||||
@Param("usageReq") String usageReq,
|
||||
@Param("postProcess") String postProcess,
|
||||
@Param("nextProcess") String nextProcess,
|
||||
@Param("sampleReq") String sampleReq,
|
||||
@Param("rawManufacturer") String rawManufacturer,
|
||||
@Param("rawMaterial") String rawMaterial,
|
||||
@Param("rawThick") BigDecimal rawThick,
|
||||
@Param("rawWidth") BigDecimal rawWidth,
|
||||
@Param("rawMaterialId") String rawMaterialId,
|
||||
@Param("rawCoilNos") String rawCoilNos,
|
||||
@Param("rawLocation") String rawLocation,
|
||||
@Param("rawPackaging") String rawPackaging,
|
||||
@Param("rawEdgeReq") String rawEdgeReq,
|
||||
@Param("rawCoatingType") String rawCoatingType,
|
||||
@Param("rawNetWeight") BigDecimal rawNetWeight,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("createBy") String createBy,
|
||||
@Param("updateBy") String updateBy);
|
||||
|
||||
int updateRow(@Param("id") Long id,
|
||||
@Param("lineId") Long lineId,
|
||||
@Param("lineName") String lineName,
|
||||
@Param("planCode") String planCode,
|
||||
@Param("planType") String planType,
|
||||
@Param("scheduler") String scheduler,
|
||||
@Param("remark") String remark,
|
||||
@Param("bizSeqNo") String bizSeqNo,
|
||||
@Param("orderCode") String orderCode,
|
||||
@Param("contractCode") String contractCode,
|
||||
@Param("customerName") String customerName,
|
||||
@Param("salesman") String salesman,
|
||||
@Param("productName") String productName,
|
||||
@Param("productMaterial") String productMaterial,
|
||||
@Param("coatingG") BigDecimal coatingG,
|
||||
@Param("productWidth") BigDecimal productWidth,
|
||||
@Param("rollingThick") BigDecimal rollingThick,
|
||||
@Param("markCoatThick") BigDecimal markCoatThick,
|
||||
@Param("tonSteelLengthRange") String tonSteelLengthRange,
|
||||
@Param("planQty") BigDecimal planQty,
|
||||
@Param("planWeight") BigDecimal planWeight,
|
||||
@Param("surfaceTreatment") String surfaceTreatment,
|
||||
@Param("widthReq") String widthReq,
|
||||
@Param("usageReq") String usageReq,
|
||||
@Param("postProcess") String postProcess,
|
||||
@Param("nextProcess") String nextProcess,
|
||||
@Param("sampleReq") String sampleReq,
|
||||
@Param("rawManufacturer") String rawManufacturer,
|
||||
@Param("rawMaterial") String rawMaterial,
|
||||
@Param("rawThick") BigDecimal rawThick,
|
||||
@Param("rawWidth") BigDecimal rawWidth,
|
||||
@Param("rawMaterialId") String rawMaterialId,
|
||||
@Param("rawCoilNos") String rawCoilNos,
|
||||
@Param("rawLocation") String rawLocation,
|
||||
@Param("rawPackaging") String rawPackaging,
|
||||
@Param("rawEdgeReq") String rawEdgeReq,
|
||||
@Param("rawCoatingType") String rawCoatingType,
|
||||
@Param("rawNetWeight") BigDecimal rawNetWeight,
|
||||
@Param("startTime") LocalDateTime startTime,
|
||||
@Param("endTime") LocalDateTime endTime,
|
||||
@Param("updateBy") String updateBy);
|
||||
|
||||
int deleteRow(@Param("id") Long id, @Param("updateBy") String updateBy);
|
||||
|
||||
int softDelete(@Param("id") Long id,
|
||||
@Param("updateBy") String updateBy);
|
||||
|
||||
int deleteById(@Param("id") Long id, @Param("updateBy") String updateBy);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.klp.aps.service;
|
||||
|
||||
import com.klp.aps.domain.dto.ApsQuickSheetQueryReq;
|
||||
import com.klp.aps.domain.dto.ApsQuickSheetSaveReq;
|
||||
import com.klp.aps.domain.vo.ApsQuickSheetRowVo;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
public interface ApsQuickSheetService {
|
||||
List<ApsQuickSheetRowVo> queryList(ApsQuickSheetQueryReq req);
|
||||
|
||||
void saveRows(ApsQuickSheetSaveReq req, String operator);
|
||||
|
||||
List<ApsQuickSheetRowVo> buildPresetRows(Long lineId, String salesman);
|
||||
|
||||
void exportExcel(ApsQuickSheetQueryReq req, HttpServletResponse response);
|
||||
|
||||
void deleteById(Long id, String operator);
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
package com.klp.aps.service.impl;
|
||||
|
||||
import com.klp.aps.domain.dto.ApsQuickSheetSaveReq;
|
||||
import com.klp.aps.domain.vo.ApsQuickSheetRowVo;
|
||||
import com.klp.aps.mapper.ApsQuickSheetMapper;
|
||||
import com.klp.aps.service.ApsQuickSheetService;
|
||||
import com.klp.common.exception.ServiceException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class ApsQuickSheetServiceImpl implements ApsQuickSheetService {
|
||||
|
||||
private final ApsQuickSheetMapper quickSheetMapper;
|
||||
|
||||
private static final DateTimeFormatter DATE_CODE = DateTimeFormatter.ofPattern("yyyyMMdd");
|
||||
|
||||
@Override
|
||||
public List<ApsQuickSheetRowVo> queryList(com.klp.aps.domain.dto.ApsQuickSheetQueryReq req) {
|
||||
return quickSheetMapper.selectList(req);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveRows(ApsQuickSheetSaveReq req, String operator) {
|
||||
if (req == null || req.getRows() == null) {
|
||||
throw new ServiceException("保存数据不能为空");
|
||||
}
|
||||
for (ApsQuickSheetSaveReq.Row row : req.getRows()) {
|
||||
if (row == null) continue;
|
||||
Long id = row.getQuickSheetId();
|
||||
Long lineId = row.getLineId();
|
||||
String lineName = row.getLineName();
|
||||
String planCode = row.getPlanCode();
|
||||
String planType = row.getPlanType();
|
||||
String scheduler = row.getScheduler();
|
||||
String remark = row.getRemark();
|
||||
|
||||
String bizSeqNo = row.getBizSeqNo();
|
||||
String orderCode = row.getOrderCode();
|
||||
String contractCode = row.getContractCode();
|
||||
String customerName = row.getCustomerName();
|
||||
String salesman = row.getSalesman();
|
||||
|
||||
String productName = row.getProductName();
|
||||
String productMaterial = row.getProductMaterial();
|
||||
BigDecimal coatingG = parseQty(row.getCoatingG());
|
||||
BigDecimal productWidth = parseQty(row.getProductWidth());
|
||||
BigDecimal rollingThick = parseQty(row.getRollingThick());
|
||||
BigDecimal markCoatThick = parseQty(row.getMarkCoatThick());
|
||||
String tonSteelLengthRange = row.getTonSteelLengthRange();
|
||||
BigDecimal planQty = parseQty(row.getPlanQty());
|
||||
BigDecimal planWeight = parseQty(row.getPlanWeight());
|
||||
String surfaceTreatment = row.getSurfaceTreatment();
|
||||
String widthReq = row.getWidthReq();
|
||||
String usageReq = row.getUsageReq();
|
||||
String postProcess = row.getPostProcess();
|
||||
String nextProcess = row.getNextProcess();
|
||||
String sampleReq = row.getSampleReq();
|
||||
|
||||
String rawManufacturer = row.getRawManufacturer();
|
||||
String rawMaterial = row.getRawMaterial();
|
||||
BigDecimal rawThick = parseQty(row.getRawThick());
|
||||
BigDecimal rawWidth = parseQty(row.getRawWidth());
|
||||
String rawMaterialId = row.getRawMaterialId();
|
||||
String rawCoilNos = row.getRawCoilNos();
|
||||
String rawLocation = row.getRawLocation();
|
||||
String rawPackaging = row.getRawPackaging();
|
||||
String rawEdgeReq = row.getRawEdgeReq();
|
||||
String rawCoatingType = row.getRawCoatingType();
|
||||
BigDecimal rawNetWeight = parseQty(row.getRawNetWeight());
|
||||
LocalDateTime startTime = parseTime(row.getStartTime());
|
||||
LocalDateTime endTime = parseTime(row.getEndTime());
|
||||
|
||||
boolean hasAny = lineId != null || isNotBlank(lineName) || isNotBlank(planCode) || isNotBlank(planType)
|
||||
|| isNotBlank(scheduler) || isNotBlank(remark)
|
||||
|| isNotBlank(bizSeqNo) || isNotBlank(orderCode) || isNotBlank(contractCode)
|
||||
|| isNotBlank(customerName) || isNotBlank(salesman)
|
||||
|| isNotBlank(productName) || isNotBlank(productMaterial)
|
||||
|| coatingG != null || productWidth != null || rollingThick != null || markCoatThick != null
|
||||
|| isNotBlank(tonSteelLengthRange) || planQty != null || planWeight != null
|
||||
|| isNotBlank(surfaceTreatment) || isNotBlank(widthReq) || isNotBlank(usageReq)
|
||||
|| isNotBlank(postProcess) || isNotBlank(nextProcess) || isNotBlank(sampleReq)
|
||||
|| isNotBlank(rawManufacturer) || isNotBlank(rawMaterial)
|
||||
|| rawThick != null || rawWidth != null
|
||||
|| isNotBlank(rawMaterialId) || isNotBlank(rawCoilNos) || isNotBlank(rawLocation)
|
||||
|| isNotBlank(rawPackaging) || isNotBlank(rawEdgeReq) || isNotBlank(rawCoatingType)
|
||||
|| rawNetWeight != null || startTime != null || endTime != null;
|
||||
if (!hasAny) {
|
||||
continue;
|
||||
}
|
||||
if (id == null) {
|
||||
if (!isNotBlank(planCode)) {
|
||||
planCode = buildPlanCode();
|
||||
}
|
||||
quickSheetMapper.insertRow(lineId, lineName, LocalDate.now(), planCode, planType, scheduler, remark,
|
||||
bizSeqNo, orderCode, contractCode, customerName, salesman,
|
||||
productName, productMaterial, coatingG, productWidth, rollingThick, markCoatThick,
|
||||
tonSteelLengthRange, planQty, planWeight, surfaceTreatment, widthReq, usageReq,
|
||||
postProcess, nextProcess, sampleReq,
|
||||
rawManufacturer, rawMaterial, rawThick, rawWidth, rawMaterialId, rawCoilNos,
|
||||
rawLocation, rawPackaging, rawEdgeReq, rawCoatingType, rawNetWeight,
|
||||
startTime, endTime, operator, operator);
|
||||
} else {
|
||||
quickSheetMapper.updateRow(id, lineId, lineName, planCode, planType, scheduler, remark,
|
||||
bizSeqNo, orderCode, contractCode, customerName, salesman,
|
||||
productName, productMaterial, coatingG, productWidth, rollingThick, markCoatThick,
|
||||
tonSteelLengthRange, planQty, planWeight, surfaceTreatment, widthReq, usageReq,
|
||||
postProcess, nextProcess, sampleReq,
|
||||
rawManufacturer, rawMaterial, rawThick, rawWidth, rawMaterialId, rawCoilNos,
|
||||
rawLocation, rawPackaging, rawEdgeReq, rawCoatingType, rawNetWeight,
|
||||
startTime, endTime, operator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ApsQuickSheetRowVo> buildPresetRows(Long lineId, String salesman) {
|
||||
List<ApsQuickSheetRowVo> rows = new ArrayList<>();
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
LocalDateTime end = now.plusHours(1);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
ApsQuickSheetRowVo row = new ApsQuickSheetRowVo();
|
||||
row.setLineId(lineId);
|
||||
row.setLineName(lineId == null ? null : ("产线" + lineId));
|
||||
row.setSalesman(salesman);
|
||||
row.setPlanQty(BigDecimal.ZERO);
|
||||
row.setStartTime(now);
|
||||
row.setEndTime(end);
|
||||
row.setPlanCode(buildPlanCode());
|
||||
rows.add(row);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportExcel(com.klp.aps.domain.dto.ApsQuickSheetQueryReq req, javax.servlet.http.HttpServletResponse response) {
|
||||
List<ApsQuickSheetRowVo> rows = queryList(req);
|
||||
try (org.apache.poi.ss.usermodel.Workbook wb = new org.apache.poi.xssf.usermodel.XSSFWorkbook()) {
|
||||
org.apache.poi.ss.usermodel.Sheet sheet = wb.createSheet("快速排产表");
|
||||
|
||||
int r = 0;
|
||||
org.apache.poi.ss.usermodel.Row title = sheet.createRow(r++);
|
||||
title.setHeightInPoints(36f);
|
||||
org.apache.poi.ss.usermodel.Cell t0 = title.createCell(0);
|
||||
t0.setCellValue("快速排产表(Excel录入)");
|
||||
sheet.addMergedRegion(new org.apache.poi.ss.util.CellRangeAddress(0, 0, 0, 39));
|
||||
|
||||
org.apache.poi.ss.usermodel.CellStyle titleStyle = wb.createCellStyle();
|
||||
titleStyle.setAlignment(org.apache.poi.ss.usermodel.HorizontalAlignment.CENTER);
|
||||
titleStyle.setVerticalAlignment(org.apache.poi.ss.usermodel.VerticalAlignment.CENTER);
|
||||
org.apache.poi.xssf.usermodel.XSSFFont titleFont = ((org.apache.poi.xssf.usermodel.XSSFWorkbook) wb).createFont();
|
||||
titleFont.setBold(true);
|
||||
titleFont.setFontHeightInPoints((short) 15);
|
||||
titleStyle.setFont(titleFont);
|
||||
t0.setCellStyle(titleStyle);
|
||||
|
||||
String[] headers = new String[]{
|
||||
"产线", "排产日期", "排产单号", "排产类型", "排产人", "修改人", "备注",
|
||||
"内容序号", "销售内容", "订单合同号", "客户", "业务员",
|
||||
"成品名称", "材质", "镀层g", "成品宽度", "轧制厚度", "标丝厚度", "吨钢长度区间m",
|
||||
"数量", "重量", "表面处理", "切边要求", "宽度要求", "用途", "后处理", "下工序", "取样",
|
||||
"厂家", "原料信息", "原料厚度mm", "宽度mm", "原料钢卷", "原料卷号", "钢卷位置", "包装要求", "镀层种类", "原料净重",
|
||||
"开始时间", "结束时间"
|
||||
};
|
||||
org.apache.poi.ss.usermodel.Row head = sheet.createRow(r++);
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
head.createCell(i).setCellValue(headers[i]);
|
||||
}
|
||||
|
||||
if (rows != null) {
|
||||
for (ApsQuickSheetRowVo row : rows) {
|
||||
org.apache.poi.ss.usermodel.Row rr = sheet.createRow(r++);
|
||||
int cc = 0;
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getLineName(), row.getLineId()));
|
||||
rr.createCell(cc++).setCellValue(row.getPlanDate() == null ? "" : row.getPlanDate().toString());
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getPlanCode(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getPlanType(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getScheduler(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getRemark(), ""));
|
||||
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getBizSeqNo(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getOrderCode(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getContractCode(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getCustomerName(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getSalesman(), ""));
|
||||
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getProductName(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getProductMaterial(), ""));
|
||||
rr.createCell(cc++).setCellValue(row.getCoatingG() == null ? "" : row.getCoatingG().toPlainString());
|
||||
rr.createCell(cc++).setCellValue(row.getProductWidth() == null ? "" : row.getProductWidth().toPlainString());
|
||||
rr.createCell(cc++).setCellValue(row.getRollingThick() == null ? "" : row.getRollingThick().toPlainString());
|
||||
rr.createCell(cc++).setCellValue(row.getMarkCoatThick() == null ? "" : row.getMarkCoatThick().toPlainString());
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getTonSteelLengthRange(), ""));
|
||||
rr.createCell(cc++).setCellValue(row.getPlanQty() == null ? "" : row.getPlanQty().toPlainString());
|
||||
rr.createCell(cc++).setCellValue(row.getPlanWeight() == null ? "" : row.getPlanWeight().toPlainString());
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getSurfaceTreatment(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getWidthReq(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getRawEdgeReq(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getUsageReq(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getPostProcess(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getNextProcess(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getSampleReq(), ""));
|
||||
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getRawManufacturer(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getRawMaterial(), ""));
|
||||
rr.createCell(cc++).setCellValue(row.getRawThick() == null ? "" : row.getRawThick().toPlainString());
|
||||
rr.createCell(cc++).setCellValue(row.getRawWidth() == null ? "" : row.getRawWidth().toPlainString());
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getRawMaterialId(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getRawCoilNos(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getRawLocation(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getRawPackaging(), ""));
|
||||
rr.createCell(cc++).setCellValue(nvl(row.getRawCoatingType(), ""));
|
||||
rr.createCell(cc++).setCellValue(row.getRawNetWeight() == null ? "" : row.getRawNetWeight().toPlainString());
|
||||
|
||||
rr.createCell(cc++).setCellValue(row.getStartTime() == null ? "" : row.getStartTime().toString());
|
||||
rr.createCell(cc++).setCellValue(row.getEndTime() == null ? "" : row.getEndTime().toString());
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
sheet.autoSizeColumn(i, true);
|
||||
int w = sheet.getColumnWidth(i);
|
||||
sheet.setColumnWidth(i, Math.min(Math.max(w, 3000), 12000));
|
||||
}
|
||||
|
||||
String filename = "aps_quick_sheet_" + System.currentTimeMillis() + ".xlsx";
|
||||
String encoded = java.net.URLEncoder.encode(filename, java.nio.charset.StandardCharsets.UTF_8.name());
|
||||
response.setCharacterEncoding(java.nio.charset.StandardCharsets.UTF_8.name());
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encoded);
|
||||
|
||||
try (javax.servlet.ServletOutputStream os = response.getOutputStream()) {
|
||||
wb.write(os);
|
||||
os.flush();
|
||||
}
|
||||
} catch (java.io.IOException e) {
|
||||
throw new ServiceException("导出失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private String buildPlanCode() {
|
||||
LocalDate today = LocalDate.now();
|
||||
int seq = quickSheetMapper.countToday(today) + 1;
|
||||
return today.format(DATE_CODE) + String.format("%03d", seq);
|
||||
}
|
||||
|
||||
private String nvl(Object v, Object fallback) {
|
||||
if (v == null) return String.valueOf(fallback);
|
||||
String s = String.valueOf(v);
|
||||
return s == null ? String.valueOf(fallback) : s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(Long id, String operator) {
|
||||
if (id == null) return;
|
||||
quickSheetMapper.deleteRow(id, operator);
|
||||
}
|
||||
|
||||
private BigDecimal parseQty(String val) {
|
||||
if (val == null || val.trim().isEmpty()) return null;
|
||||
try {
|
||||
return new BigDecimal(val.trim());
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNotBlank(String val) {
|
||||
return val != null && !val.trim().isEmpty();
|
||||
}
|
||||
|
||||
private LocalDateTime parseTime(String val) {
|
||||
if (val == null || val.trim().isEmpty()) return null;
|
||||
String v = val.trim();
|
||||
if (v.length() == 16) v = v + ":00";
|
||||
try {
|
||||
return LocalDateTime.parse(v, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,176 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.klp.aps.mapper.ApsQuickSheetMapper">
|
||||
|
||||
<select id="selectList" parameterType="com.klp.aps.domain.dto.ApsQuickSheetQueryReq" resultType="com.klp.aps.domain.vo.ApsQuickSheetRowVo">
|
||||
SELECT
|
||||
quick_sheet_id AS quickSheetId,
|
||||
plan_date AS planDate,
|
||||
line_id AS lineId,
|
||||
line_name AS lineName,
|
||||
plan_code AS planCode,
|
||||
plan_type AS planType,
|
||||
scheduler,
|
||||
remark,
|
||||
biz_seq_no AS bizSeqNo,
|
||||
order_code AS orderCode,
|
||||
contract_code AS contractCode,
|
||||
customer_name AS customerName,
|
||||
salesman,
|
||||
product_name AS productName,
|
||||
product_material AS productMaterial,
|
||||
coating_g AS coatingG,
|
||||
product_width AS productWidth,
|
||||
rolling_thick AS rollingThick,
|
||||
mark_coat_thick AS markCoatThick,
|
||||
ton_steel_length_range AS tonSteelLengthRange,
|
||||
plan_qty AS planQty,
|
||||
plan_weight AS planWeight,
|
||||
surface_treatment AS surfaceTreatment,
|
||||
width_req AS widthReq,
|
||||
usage_req AS usageReq,
|
||||
post_process AS postProcess,
|
||||
next_process AS nextProcess,
|
||||
sample_req AS sampleReq,
|
||||
raw_manufacturer AS rawManufacturer,
|
||||
raw_material AS rawMaterial,
|
||||
raw_thick AS rawThick,
|
||||
raw_width AS rawWidth,
|
||||
raw_material_id AS rawMaterialId,
|
||||
raw_coil_nos AS rawCoilNos,
|
||||
raw_location AS rawLocation,
|
||||
raw_packaging AS rawPackaging,
|
||||
raw_edge_req AS rawEdgeReq,
|
||||
raw_coating_type AS rawCoatingType,
|
||||
raw_net_weight AS rawNetWeight,
|
||||
start_time AS startTime,
|
||||
end_time AS endTime
|
||||
FROM aps_quick_sheet
|
||||
WHERE del_flag = 0
|
||||
<if test="startDate != null">
|
||||
AND start_time <![CDATA[>=]]> CONCAT(#{startDate}, ' 00:00:00')
|
||||
</if>
|
||||
<if test="endDate != null">
|
||||
AND start_time <![CDATA[<=]]> CONCAT(#{endDate}, ' 23:59:59')
|
||||
</if>
|
||||
<if test="lineId != null">
|
||||
AND line_id = #{lineId}
|
||||
</if>
|
||||
<if test="customerName != null and customerName != ''">
|
||||
AND customer_name LIKE CONCAT('%', #{customerName}, '%')
|
||||
</if>
|
||||
ORDER BY quick_sheet_id DESC
|
||||
</select>
|
||||
|
||||
<select id="countToday" resultType="int">
|
||||
SELECT COUNT(1)
|
||||
FROM aps_quick_sheet
|
||||
WHERE del_flag = 0
|
||||
AND plan_date = #{planDate}
|
||||
</select>
|
||||
|
||||
<select id="selectIdByPlanCode" resultType="long">
|
||||
SELECT quick_sheet_id
|
||||
FROM aps_quick_sheet
|
||||
WHERE plan_code = #{planCode}
|
||||
AND del_flag = 0
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<insert id="insertRow">
|
||||
INSERT INTO aps_quick_sheet (
|
||||
line_id, line_name, plan_date, plan_code, plan_type, scheduler, remark,
|
||||
biz_seq_no, order_code, contract_code, customer_name, salesman,
|
||||
product_name, product_material, coating_g, product_width, rolling_thick, mark_coat_thick,
|
||||
ton_steel_length_range, plan_qty, plan_weight, surface_treatment, width_req, usage_req,
|
||||
post_process, next_process, sample_req,
|
||||
raw_manufacturer, raw_material, raw_thick, raw_width, raw_material_id, raw_coil_nos,
|
||||
raw_location, raw_packaging, raw_edge_req, raw_coating_type, raw_net_weight,
|
||||
start_time, end_time,
|
||||
create_by, update_by, create_time, update_time, del_flag
|
||||
) VALUES (
|
||||
#{lineId}, #{lineName}, #{planDate}, #{planCode}, #{planType}, #{scheduler}, #{remark},
|
||||
#{bizSeqNo}, #{orderCode}, #{contractCode}, #{customerName}, #{salesman},
|
||||
#{productName}, #{productMaterial}, #{coatingG}, #{productWidth}, #{rollingThick}, #{markCoatThick},
|
||||
#{tonSteelLengthRange}, #{planQty}, #{planWeight}, #{surfaceTreatment}, #{widthReq}, #{usageReq},
|
||||
#{postProcess}, #{nextProcess}, #{sampleReq},
|
||||
#{rawManufacturer}, #{rawMaterial}, #{rawThick}, #{rawWidth}, #{rawMaterialId}, #{rawCoilNos},
|
||||
#{rawLocation}, #{rawPackaging}, #{rawEdgeReq}, #{rawCoatingType}, #{rawNetWeight},
|
||||
#{startTime}, #{endTime},
|
||||
#{createBy}, #{updateBy}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 0
|
||||
)
|
||||
</insert>
|
||||
|
||||
<update id="updateRow">
|
||||
UPDATE aps_quick_sheet
|
||||
SET line_id = #{lineId},
|
||||
line_name = #{lineName},
|
||||
plan_code = #{planCode},
|
||||
plan_type = #{planType},
|
||||
scheduler = #{scheduler},
|
||||
remark = #{remark},
|
||||
biz_seq_no = #{bizSeqNo},
|
||||
order_code = #{orderCode},
|
||||
contract_code = #{contractCode},
|
||||
customer_name = #{customerName},
|
||||
salesman = #{salesman},
|
||||
product_name = #{productName},
|
||||
product_material = #{productMaterial},
|
||||
coating_g = #{coatingG},
|
||||
product_width = #{productWidth},
|
||||
rolling_thick = #{rollingThick},
|
||||
mark_coat_thick = #{markCoatThick},
|
||||
ton_steel_length_range = #{tonSteelLengthRange},
|
||||
plan_qty = #{planQty},
|
||||
plan_weight = #{planWeight},
|
||||
surface_treatment = #{surfaceTreatment},
|
||||
width_req = #{widthReq},
|
||||
usage_req = #{usageReq},
|
||||
post_process = #{postProcess},
|
||||
next_process = #{nextProcess},
|
||||
sample_req = #{sampleReq},
|
||||
raw_manufacturer = #{rawManufacturer},
|
||||
raw_material = #{rawMaterial},
|
||||
raw_thick = #{rawThick},
|
||||
raw_width = #{rawWidth},
|
||||
raw_material_id = #{rawMaterialId},
|
||||
raw_coil_nos = #{rawCoilNos},
|
||||
raw_location = #{rawLocation},
|
||||
raw_packaging = #{rawPackaging},
|
||||
raw_edge_req = #{rawEdgeReq},
|
||||
raw_coating_type = #{rawCoatingType},
|
||||
raw_net_weight = #{rawNetWeight},
|
||||
start_time = #{startTime},
|
||||
end_time = #{endTime},
|
||||
update_by = #{updateBy},
|
||||
update_time = CURRENT_TIMESTAMP
|
||||
WHERE quick_sheet_id = #{id}
|
||||
</update>
|
||||
|
||||
<update id="deleteRow">
|
||||
UPDATE aps_quick_sheet
|
||||
SET del_flag = 1,
|
||||
update_by = #{updateBy},
|
||||
update_time = CURRENT_TIMESTAMP
|
||||
WHERE quick_sheet_id = #{id}
|
||||
</update>
|
||||
|
||||
<update id="softDelete">
|
||||
UPDATE aps_quick_sheet
|
||||
SET del_flag = 1,
|
||||
update_by = #{updateBy},
|
||||
update_time = CURRENT_TIMESTAMP
|
||||
WHERE quick_sheet_id = #{id}
|
||||
</update>
|
||||
|
||||
<update id="deleteById">
|
||||
UPDATE aps_quick_sheet
|
||||
SET del_flag = 1,
|
||||
update_by = #{updateBy},
|
||||
update_time = CURRENT_TIMESTAMP
|
||||
WHERE quick_sheet_id = #{id}
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
@@ -170,6 +170,16 @@
|
||||
<artifactId>ip2region</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 引入flyway -->
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-core</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.flywaydb</groupId>
|
||||
<artifactId>flyway-mysql</artifactId>
|
||||
</dependency>
|
||||
<!-- 动态数据源依赖 -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.klp.common.config;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
|
||||
import org.flywaydb.core.Flyway;
|
||||
import org.flywaydb.core.api.output.MigrateResult;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class FlywayConfig {
|
||||
|
||||
@Value("${spring.profiles.active}")
|
||||
private String activeProfile;
|
||||
|
||||
@Value("${spring.flyway.baseline-on-migrate}")
|
||||
private boolean baselineOnMigrate;
|
||||
|
||||
@Value("${spring.flyway.locations}")
|
||||
private String locations;
|
||||
|
||||
@Value("${spring.flyway.table}")
|
||||
private String table;
|
||||
|
||||
@Resource
|
||||
private DataSource dataSource;
|
||||
|
||||
@Bean
|
||||
public Flyway flyway() {
|
||||
DataSource masterDataSource = ((DynamicRoutingDataSource) dataSource).getDataSource("master");
|
||||
System.out.println("masterDataSource class: " + masterDataSource.getClass().getName());
|
||||
|
||||
// // 如果想显式拿底层 HikariDataSource
|
||||
// if (masterDataSource instanceof ItemDataSource) {
|
||||
// masterDataSource = ((ItemDataSource) masterDataSource).getRealDataSource();
|
||||
// }
|
||||
|
||||
System.out.println("masterDataSource class: " + masterDataSource.getClass().getName());
|
||||
return Flyway.configure()
|
||||
.dataSource(masterDataSource) // 注意这里是真实主库 DataSource
|
||||
.baselineOnMigrate(baselineOnMigrate)
|
||||
.locations(locations)
|
||||
.table(table)
|
||||
.load();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CommandLineRunner flywayRunner(Flyway flyway) {
|
||||
return args -> {
|
||||
System.out.println("========== 当前环境: " + activeProfile + " ==========");
|
||||
System.out.println("========== 开始执行 Flyway 数据库迁移 ==========");
|
||||
|
||||
MigrateResult result = flyway.migrate();
|
||||
System.out.println("迁移成功版本数: " + result.migrationsExecuted);
|
||||
|
||||
result.migrations.forEach(m -> {
|
||||
System.out.println("执行版本: " + m.version + ",描述: " + m.description);
|
||||
});
|
||||
|
||||
System.out.println("========== Flyway 数据库迁移完成 ==========");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.klp.pocket.acid.domain.vo.AcidOeeIdealCycleVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeLoss7Vo;
|
||||
import com.klp.pocket.acid.domain.vo.Klptcm1ProStoppageVo;
|
||||
import com.klp.pocket.acid.service.IAcidOeeService;
|
||||
import com.klp.pocket.galvanize1.service.IGalvanizeOeeService;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
|
||||
@@ -62,7 +61,6 @@ import java.util.stream.Collectors;
|
||||
public class OeeReportController extends BaseController {
|
||||
|
||||
private final IAcidOeeService acidOeeService;
|
||||
private final IGalvanizeOeeService galvanizeOeeService;
|
||||
private final StringRedisTemplate stringRedisTemplate;
|
||||
private final OeeReportJobService oeeReportJobService;
|
||||
private final OeeWordAiAnalysisService oeeWordAiAnalysisService;
|
||||
@@ -78,20 +76,26 @@ public class OeeReportController extends BaseController {
|
||||
*
|
||||
* 路由:GET /oee/line/acid/summary
|
||||
* 说明:
|
||||
* - 支持 startDate/endDate 参数(yyyy-MM-dd);
|
||||
* - 若不传则默认查询当前月份(1号~今天);
|
||||
* - 仅实时计算,不走缓存。
|
||||
* - 不接受 start/end 参数,固定返回“当前月份(1号~今天)”的当月预计算结果;
|
||||
* - 优先从 Redis 当月缓存读取;若缓存缺失则实时计算一次当前月。
|
||||
*/
|
||||
@GetMapping("/acid/summary")
|
||||
public R<List<AcidOeeDailySummaryVo>> getAcidSummary(
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate,
|
||||
@RequestParam(required = false, defaultValue = "acid") String lineType
|
||||
) {
|
||||
String[] range = resolveDateRange(startDate, endDate);
|
||||
List<AcidOeeDailySummaryVo> dailyList = isGalvanize(lineType)
|
||||
? galvanizeOeeService.getDailySummary(range[0], range[1])
|
||||
: acidOeeService.getDailySummary(range[0], range[1]);
|
||||
public R<List<AcidOeeDailySummaryVo>> getAcidSummary() {
|
||||
String yyyyMM = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
|
||||
String summaryKey = String.format("oee:report:month:summary:%s:SY", yyyyMM);
|
||||
|
||||
// 1. 优先从 Redis 读取当月预计算结果
|
||||
String json = stringRedisTemplate.opsForValue().get(summaryKey);
|
||||
if (StringUtils.isNotBlank(json)) {
|
||||
List<AcidOeeDailySummaryVo> cached =
|
||||
JSON.parseArray(json, AcidOeeDailySummaryVo.class);
|
||||
return R.ok(cached);
|
||||
}
|
||||
|
||||
// 2. 缓存缺失时,回退为实时计算当前月
|
||||
String[] range = resolveDateRange(null, null);
|
||||
List<AcidOeeDailySummaryVo> dailyList =
|
||||
acidOeeService.getDailySummary(range[0], range[1]);
|
||||
return R.ok(dailyList);
|
||||
}
|
||||
|
||||
@@ -105,15 +109,21 @@ public class OeeReportController extends BaseController {
|
||||
*/
|
||||
@GetMapping("/acid/loss7")
|
||||
public R<List<AcidOeeLoss7Vo>> getAcidLoss7(
|
||||
@RequestParam(required = false, defaultValue = "50") Integer topN,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate,
|
||||
@RequestParam(required = false, defaultValue = "acid") String lineType
|
||||
@RequestParam(required = false, defaultValue = "50") Integer topN
|
||||
) {
|
||||
String[] range = resolveDateRange(startDate, endDate);
|
||||
List<AcidOeeLoss7Vo> lossList = isGalvanize(lineType)
|
||||
? galvanizeOeeService.getLoss7Summary(range[0], range[1])
|
||||
: acidOeeService.getLoss7Summary(range[0], range[1]);
|
||||
String yyyyMM = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMM"));
|
||||
String loss7Key = String.format("oee:report:month:loss7:%s:SY", yyyyMM);
|
||||
|
||||
// 1. 优先从 Redis 读取当月 7 大损失预计算结果
|
||||
String json = stringRedisTemplate.opsForValue().get(loss7Key);
|
||||
List<AcidOeeLoss7Vo> lossList;
|
||||
if (StringUtils.isNotBlank(json)) {
|
||||
lossList = JSON.parseArray(json, AcidOeeLoss7Vo.class);
|
||||
} else {
|
||||
// 2. 缓存缺失时,回退为实时计算当前月
|
||||
String[] range = resolveDateRange(null, null);
|
||||
lossList = acidOeeService.getLoss7Summary(range[0], range[1]);
|
||||
}
|
||||
|
||||
if (topN != null && topN > 0 && lossList.size() > topN) {
|
||||
lossList = new ArrayList<>(lossList.subList(0, topN));
|
||||
@@ -137,16 +147,14 @@ public class OeeReportController extends BaseController {
|
||||
@RequestParam(required = false) String stopType,
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false, defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false, defaultValue = "acid") String lineType
|
||||
@RequestParam(required = false, defaultValue = "10") Integer pageSize
|
||||
) {
|
||||
// 事件明细底层按「日期」查询,这里从时间字符串中截取日期部分
|
||||
String startDate = extractDateOrDefault(startTime, true);
|
||||
String endDate = extractDateOrDefault(endTime, false);
|
||||
|
||||
List<Klptcm1ProStoppageVo> events = isGalvanize(lineType)
|
||||
? galvanizeOeeService.getStoppageEvents(startDate, endDate)
|
||||
: acidOeeService.getStoppageEvents(startDate, endDate);
|
||||
List<Klptcm1ProStoppageVo> events =
|
||||
acidOeeService.getStoppageEvents(startDate, endDate);
|
||||
|
||||
// 业务筛选:stopType、关键字(目前对 stopType / remark 做 contains 匹配)
|
||||
List<Klptcm1ProStoppageVo> filtered = events.stream()
|
||||
@@ -192,12 +200,9 @@ public class OeeReportController extends BaseController {
|
||||
@GetMapping("/acid/idealCycle")
|
||||
public R<AcidOeeIdealCycleVo> getAcidIdealCycle(
|
||||
@RequestParam String startDate,
|
||||
@RequestParam String endDate,
|
||||
@RequestParam(required = false, defaultValue = "acid") String lineType
|
||||
@RequestParam String endDate
|
||||
) {
|
||||
AcidOeeIdealCycleVo data = isGalvanize(lineType)
|
||||
? galvanizeOeeService.getIdealCycle(startDate, endDate)
|
||||
: acidOeeService.getIdealCycle(startDate, endDate);
|
||||
AcidOeeIdealCycleVo data = acidOeeService.getIdealCycle(startDate, endDate);
|
||||
return R.ok(data);
|
||||
}
|
||||
|
||||
@@ -418,10 +423,6 @@ public class OeeReportController extends BaseController {
|
||||
/**
|
||||
* 若未显式传入日期范围,则默认当前月 [1号, 今天]。
|
||||
*/
|
||||
private boolean isGalvanize(String lineType) {
|
||||
return "galvanize1".equalsIgnoreCase(lineType) || "galvanize".equalsIgnoreCase(lineType) || "dx".equalsIgnoreCase(lineType);
|
||||
}
|
||||
|
||||
private String[] resolveDateRange(String startDate, String endDate) {
|
||||
if (StringUtils.isNotBlank(startDate) && StringUtils.isNotBlank(endDate)) {
|
||||
return new String[]{startDate, endDate};
|
||||
|
||||
137
klp-da/src/main/java/com/klp/da/task/AcidOeeMonthTask.java
Normal file
137
klp-da/src/main/java/com/klp/da/task/AcidOeeMonthTask.java
Normal file
@@ -0,0 +1,137 @@
|
||||
package com.klp.da.task;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo;
|
||||
import com.klp.pocket.acid.service.IAcidOeeService;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 酸轧线 OEE 当月预计算任务
|
||||
*
|
||||
* 需求对应 docs/oee-report-design.md 第 12.2 节:
|
||||
* - 项目启动完成后即计算当月 OEE 聚合结果并写入 Redis;
|
||||
* - 每天凌晨 04:00 重新计算当月数据并覆盖缓存。
|
||||
*
|
||||
* 当前仅实现酸轧线(SY)的当月日汇总预计算;
|
||||
* key 约定:
|
||||
* - 汇总结果:oee:report:month:summary:{yyyyMM}:SY
|
||||
* - 元信息: oee:report:month:meta:{yyyyMM}:SY
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Component
|
||||
public class AcidOeeMonthTask {
|
||||
|
||||
/** Redis 缓存 key 模板:当月 OEE 汇总(酸轧线) */
|
||||
private static final String SUMMARY_KEY_PATTERN = "oee:report:month:summary:%s:SY";
|
||||
|
||||
/** Redis 缓存 key 模板:当月元信息(酸轧线) */
|
||||
private static final String META_KEY_PATTERN = "oee:report:month:meta:%s:SY";
|
||||
|
||||
private static final DateTimeFormatter YEAR_MONTH_FMT = DateTimeFormatter.ofPattern("yyyyMM");
|
||||
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ISO_DATE;
|
||||
private static final DateTimeFormatter DATE_TIME_FMT = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
|
||||
|
||||
private final IAcidOeeService acidOeeService;
|
||||
private final StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/**
|
||||
* 项目启动完成后立即计算一次当月酸轧 OEE 汇总并写入 Redis。
|
||||
*/
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
try {
|
||||
computeCurrentMonth("startup");
|
||||
} catch (Exception e) {
|
||||
log.error("[AcidOeeMonthTask] startup compute failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 每天凌晨 04:00 重新计算当月酸轧 OEE 汇总并覆盖 Redis 缓存。
|
||||
*/
|
||||
@Scheduled(cron = "0 0 4 * * ?")
|
||||
public void scheduleDaily() {
|
||||
try {
|
||||
computeCurrentMonth("schedule-04");
|
||||
} catch (Exception e) {
|
||||
log.error("[AcidOeeMonthTask] 4am compute failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算当前月份(从当月1号到今天)的酸轧 OEE 日汇总,并写入 Redis。
|
||||
*
|
||||
* @param trigger 触发来源标记(startup / schedule-04 等)
|
||||
*/
|
||||
private void computeCurrentMonth(String trigger) {
|
||||
long startNs = System.nanoTime();
|
||||
|
||||
LocalDate now = LocalDate.now();
|
||||
String yyyyMM = now.format(YEAR_MONTH_FMT);
|
||||
|
||||
LocalDate startDate = now.withDayOfMonth(1);
|
||||
LocalDate endDate = now;
|
||||
|
||||
String startStr = startDate.format(DATE_FMT);
|
||||
String endStr = endDate.format(DATE_FMT);
|
||||
|
||||
log.info("[AcidOeeMonthTask] trigger={}, computing acid OEE month summary for {} ({} ~ {})",
|
||||
trigger, yyyyMM, startStr, endStr);
|
||||
|
||||
// 1. 调用 pocket 的 AcidOeeService 获取当月日汇总
|
||||
List<AcidOeeDailySummaryVo> dailySummaryList = acidOeeService.getDailySummary(startStr, endStr);
|
||||
|
||||
// 2. 写入 Redis(summary)
|
||||
String summaryKey = String.format(SUMMARY_KEY_PATTERN, yyyyMM);
|
||||
String summaryJson = JSON.toJSONString(dailySummaryList);
|
||||
stringRedisTemplate.opsForValue().set(summaryKey, summaryJson, 1, TimeUnit.DAYS);
|
||||
|
||||
long durationMs = (System.nanoTime() - startNs) / 1_000_000L;
|
||||
|
||||
// 3. 写入 Redis(meta)
|
||||
Meta meta = new Meta();
|
||||
meta.setComputedAt(LocalDateTime.now().format(DATE_TIME_FMT));
|
||||
meta.setDurationMs(durationMs);
|
||||
meta.setStartDate(startStr);
|
||||
meta.setEndDate(endStr);
|
||||
meta.setTrigger(trigger);
|
||||
|
||||
String metaKey = String.format(META_KEY_PATTERN, yyyyMM);
|
||||
stringRedisTemplate.opsForValue().set(metaKey, JSON.toJSONString(meta), 1, TimeUnit.DAYS);
|
||||
|
||||
log.info("[AcidOeeMonthTask] compute finish for {} dailySize={}, durationMs={}ms, summaryKey={}",
|
||||
yyyyMM, dailySummaryList.size(), durationMs, summaryKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当月预计算元信息
|
||||
*/
|
||||
@Data
|
||||
private static class Meta {
|
||||
/** 计算完成时间(ISO-8601 字符串) */
|
||||
private String computedAt;
|
||||
/** 计算耗时(毫秒) */
|
||||
private long durationMs;
|
||||
/** 统计起始日期(yyyy-MM-dd) */
|
||||
private String startDate;
|
||||
/** 统计结束日期(yyyy-MM-dd) */
|
||||
private String endDate;
|
||||
/** 触发来源(startup / schedule-04 等) */
|
||||
private String trigger;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.flywaydb.core.internal.util.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import com.klp.common.annotation.RepeatSubmit;
|
||||
|
||||
@@ -44,10 +44,6 @@ public class EqpAuxiliaryMaterial extends BaseEntity {
|
||||
* 关联设备ID(可为空,通用辅料)
|
||||
*/
|
||||
private Long equipmentId;
|
||||
/**
|
||||
* 机组(如:1#机组、2#机组、公用机组等)
|
||||
*/
|
||||
private String unitTeam;
|
||||
/**
|
||||
* 当前库存数量
|
||||
*/
|
||||
|
||||
@@ -44,10 +44,6 @@ public class EqpSparePart extends BaseEntity {
|
||||
* 关联设备ID(可为空,通用备件)
|
||||
*/
|
||||
private Long equipmentId;
|
||||
/**
|
||||
* 机组(如:1#机组、2#机组、公用机组等)
|
||||
*/
|
||||
private String unitTeam;
|
||||
/**
|
||||
* 当前库存数量
|
||||
*/
|
||||
|
||||
@@ -47,11 +47,6 @@ public class EqpAuxiliaryMaterialBo extends BaseEntity {
|
||||
*/
|
||||
private Long equipmentId;
|
||||
|
||||
/**
|
||||
* 机组(如:1#机组、2#机组、公用机组等)
|
||||
*/
|
||||
private String unitTeam;
|
||||
|
||||
/**
|
||||
* 当前库存数量
|
||||
*/
|
||||
|
||||
@@ -47,11 +47,6 @@ public class EqpSparePartBo extends BaseEntity {
|
||||
*/
|
||||
private Long equipmentId;
|
||||
|
||||
/**
|
||||
* 机组(如:1#机组、2#机组、公用机组等)
|
||||
*/
|
||||
private String unitTeam;
|
||||
|
||||
/**
|
||||
* 当前库存数量
|
||||
*/
|
||||
|
||||
@@ -56,12 +56,6 @@ public class EqpAuxiliaryMaterialVo {
|
||||
@ExcelDictFormat(readConverterExp = "可=为空,通用辅料")
|
||||
private Long equipmentId;
|
||||
|
||||
/**
|
||||
* 机组(如:1#机组、2#机组、公用机组等)
|
||||
*/
|
||||
@ExcelProperty(value = "机组")
|
||||
private String unitTeam;
|
||||
|
||||
/**
|
||||
* 当前库存数量
|
||||
*/
|
||||
|
||||
@@ -56,12 +56,6 @@ public class EqpSparePartVo {
|
||||
@ExcelDictFormat(readConverterExp = "可=为空,通用备件")
|
||||
private Long equipmentId;
|
||||
|
||||
/**
|
||||
* 机组(如:1#机组、2#机组、公用机组等)
|
||||
*/
|
||||
@ExcelProperty(value = "机组")
|
||||
private String unitTeam;
|
||||
|
||||
/**
|
||||
* 当前库存数量
|
||||
*/
|
||||
|
||||
@@ -66,7 +66,6 @@ public class EqpAuxiliaryMaterialServiceImpl implements IEqpAuxiliaryMaterialSer
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getAuxiliaryModel()), EqpAuxiliaryMaterial::getAuxiliaryModel, bo.getAuxiliaryModel());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getUnit()), EqpAuxiliaryMaterial::getUnit, bo.getUnit());
|
||||
lqw.eq(bo.getEquipmentId() != null, EqpAuxiliaryMaterial::getEquipmentId, bo.getEquipmentId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getUnitTeam()), EqpAuxiliaryMaterial::getUnitTeam, bo.getUnitTeam());
|
||||
lqw.eq(bo.getQuantity() != null, EqpAuxiliaryMaterial::getQuantity, bo.getQuantity());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,6 @@ public class EqpSparePartServiceImpl implements IEqpSparePartService {
|
||||
qw.eq(StringUtils.isNotBlank(bo.getModel()), "sp.model", bo.getModel());
|
||||
qw.eq(StringUtils.isNotBlank(bo.getUnit()), "sp.unit", bo.getUnit());
|
||||
qw.eq(bo.getEquipmentId() != null, "sp.equipment_id", bo.getEquipmentId());
|
||||
qw.like(StringUtils.isNotBlank(bo.getUnitTeam()), "sp.unit_team", bo.getUnitTeam());
|
||||
qw.eq(bo.getQuantity() != null, "sp.quantity", bo.getQuantity());
|
||||
//逻辑删除
|
||||
qw.eq("sp.del_flag", 0);
|
||||
@@ -82,7 +81,6 @@ public class EqpSparePartServiceImpl implements IEqpSparePartService {
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getModel()), EqpSparePart::getModel, bo.getModel());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getUnit()), EqpSparePart::getUnit, bo.getUnit());
|
||||
lqw.eq(bo.getEquipmentId() != null, EqpSparePart::getEquipmentId, bo.getEquipmentId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getUnitTeam()), EqpSparePart::getUnitTeam, bo.getUnitTeam());
|
||||
lqw.eq(bo.getQuantity() != null, EqpSparePart::getQuantity, bo.getQuantity());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
<result property="auxiliaryModel" column="auxiliary_model"/>
|
||||
<result property="unit" column="unit"/>
|
||||
<result property="equipmentId" column="equipment_id"/>
|
||||
<result property="unitTeam" column="unit_team"/>
|
||||
<result property="quantity" column="quantity"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
<result property="model" column="model"/>
|
||||
<result property="unit" column="unit"/>
|
||||
<result property="equipmentId" column="equipment_id"/>
|
||||
<result property="unitTeam" column="unit_team"/>
|
||||
<result property="quantity" column="quantity"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="updateBy" column="update_by"/>
|
||||
@@ -28,7 +27,6 @@
|
||||
sp.model,
|
||||
sp.unit,
|
||||
sp.equipment_id,
|
||||
sp.unit_team,
|
||||
em.equipment_name AS equipmentName,
|
||||
sp.quantity,
|
||||
sp.remark
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
package com.klp.pocket.acid.domain.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 酸轧OEE按日钢卷信息(主库来源)。
|
||||
*/
|
||||
@Data
|
||||
public class AcidOeeCoilInfoByDateVo {
|
||||
|
||||
/** 统计日期 yyyy-MM-dd */
|
||||
private String statDate;
|
||||
|
||||
/** 当前钢卷号 */
|
||||
private String coilNo;
|
||||
|
||||
/** 重量(吨) */
|
||||
private BigDecimal weight;
|
||||
|
||||
/** 判级 */
|
||||
private String qualityStatus;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.klp.pocket.acid.domain.vo;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 酸轧线OEE日汇总视图对象
|
||||
@@ -44,36 +45,18 @@ public class AcidOeeDailySummaryVo {
|
||||
/** 总产量(卷) */
|
||||
private Long totalOutputCoil;
|
||||
|
||||
/** 良品量(A系列,吨) */
|
||||
/** 良品量(吨) */
|
||||
private BigDecimal goodOutputTon;
|
||||
|
||||
/** 良品量(A系列,卷) */
|
||||
/** 良品量(卷) */
|
||||
private Long goodOutputCoil;
|
||||
|
||||
/** 合格品量(B系列,吨) */
|
||||
private BigDecimal qualifiedOutputTon;
|
||||
|
||||
/** 合格品量(B系列,卷) */
|
||||
private Long qualifiedOutputCoil;
|
||||
|
||||
/** 合格品合计(A+B,吨) */
|
||||
private BigDecimal abOutputTon;
|
||||
|
||||
/** 合格品合计(A+B,卷) */
|
||||
private Long abOutputCoil;
|
||||
|
||||
/** 次品量(C/D系列,吨) */
|
||||
/** 不良量(吨)= total_output_ton - good_output_ton */
|
||||
private BigDecimal defectOutputTon;
|
||||
|
||||
/** 次品量(C/D系列,卷) */
|
||||
/** 不良量(卷)= total_output_coil - good_output_coil */
|
||||
private Long defectOutputCoil;
|
||||
|
||||
/** 待判级量(O系列,吨) */
|
||||
private BigDecimal pendingOutputTon;
|
||||
|
||||
/** 待判级量(O系列,卷) */
|
||||
private Long pendingOutputCoil;
|
||||
|
||||
/** 理论节拍(min/吨;回归斜率) */
|
||||
private BigDecimal idealCycleTimeMinPerTon;
|
||||
|
||||
@@ -89,18 +72,9 @@ public class AcidOeeDailySummaryVo {
|
||||
/** 派生指标:性能稼动率(卷维度) */
|
||||
private BigDecimal performanceCoil;
|
||||
|
||||
/** 派生指标:良品率(A/总量) */
|
||||
/** 派生指标:良品率 */
|
||||
private BigDecimal quality;
|
||||
|
||||
/** 派生指标:合格品率(A+B/总量) */
|
||||
private BigDecimal qualifiedRate;
|
||||
|
||||
/** 派生指标:次品率(C/D系列/总量) */
|
||||
private BigDecimal defectRate;
|
||||
|
||||
/** 派生指标:待判级率(O系列/总量) */
|
||||
private BigDecimal pendingRate;
|
||||
|
||||
/** 派生指标:OEE(建议以吨维度为主) */
|
||||
private BigDecimal oee;
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.klp.pocket.acid.mapper;
|
||||
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 酸轧线OEE酸轧库Mapper。
|
||||
*/
|
||||
@Mapper
|
||||
@DS("acid")
|
||||
public interface AcidOeeAcidMapper {
|
||||
|
||||
/**
|
||||
* 查询卷级生产节拍(min/吨),用于理论节拍计算。
|
||||
*/
|
||||
List<BigDecimal> selectCoilCycleMinPerTon(@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.klp.pocket.acid.mapper;
|
||||
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 酸轧线OEE Mapper接口
|
||||
*
|
||||
* @author klp
|
||||
* @date 2026-01-30
|
||||
*/
|
||||
@Mapper
|
||||
public interface AcidOeeMapper {
|
||||
|
||||
/**
|
||||
* 查询OEE日汇总(按日期范围)
|
||||
* 聚合产量(吨/卷)、停机时间等
|
||||
*
|
||||
* @param startDate 开始日期(yyyy-MM-dd)
|
||||
* @param endDate 结束日期(yyyy-MM-dd)
|
||||
* @return 日汇总列表
|
||||
*/
|
||||
List<AcidOeeDailySummaryVo> selectDailySummary(@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate);
|
||||
|
||||
/**
|
||||
* 查询每日的钢卷号和重量(用于良品/次品判定)
|
||||
*
|
||||
* @param startDate 开始日期(yyyy-MM-dd)
|
||||
* @param endDate 结束日期(yyyy-MM-dd)
|
||||
* @return Map列表,key为日期,value为卷号和重量信息
|
||||
*/
|
||||
List<CoilInfoByDate> selectCoilInfoByDate(@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate);
|
||||
|
||||
/**
|
||||
* 查询卷级生产节拍(min/吨),用于理论节拍计算。
|
||||
*
|
||||
* @param startDate 开始日期(yyyy-MM-dd)
|
||||
* @param endDate 结束日期(yyyy-MM-dd)
|
||||
* @return 每卷的生产节拍列表(min/吨)
|
||||
*/
|
||||
List<java.math.BigDecimal> selectCoilCycleMinPerTon(@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate);
|
||||
|
||||
/**
|
||||
* 卷号信息内部类(用于Mapper返回)
|
||||
*/
|
||||
class CoilInfoByDate {
|
||||
private String statDate;
|
||||
private String coilNo;
|
||||
private java.math.BigDecimal weight;
|
||||
|
||||
public String getStatDate() { return statDate; }
|
||||
public void setStatDate(String statDate) { this.statDate = statDate; }
|
||||
public String getCoilNo() { return coilNo; }
|
||||
public void setCoilNo(String coilNo) { this.coilNo = coilNo; }
|
||||
public java.math.BigDecimal getWeight() { return weight; }
|
||||
public void setWeight(java.math.BigDecimal weight) { this.weight = weight; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package com.klp.pocket.acid.mapper;
|
||||
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeCoilInfoByDateVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 酸轧线OEE主库Mapper。
|
||||
*/
|
||||
@Mapper
|
||||
@DS("master")
|
||||
public interface AcidOeeMasterMapper {
|
||||
|
||||
/**
|
||||
* 查询OEE日汇总(总产量来自主库 wms_material_coil)。
|
||||
*/
|
||||
List<AcidOeeDailySummaryVo> selectDailySummary(@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate,
|
||||
@Param("createBy") String createBy);
|
||||
|
||||
/**
|
||||
* 查询每日钢卷重量与判级(来自主库 wms_material_coil)。
|
||||
*/
|
||||
List<AcidOeeCoilInfoByDateVo> selectCoilInfoByDate(@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate,
|
||||
@Param("createBy") String createBy);
|
||||
}
|
||||
|
||||
@@ -1,34 +1,25 @@
|
||||
package com.klp.pocket.acid.service.impl;
|
||||
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import com.klp.common.utils.StringUtils;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeCoilInfoByDateVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeIdealCycleVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeLoss7Vo;
|
||||
import com.klp.pocket.acid.domain.vo.Klptcm1ProStoppageVo;
|
||||
import com.klp.pocket.acid.domain.bo.Klptcm1ProStoppageBo;
|
||||
import com.klp.pocket.acid.mapper.AcidOeeMasterMapper;
|
||||
import com.klp.pocket.acid.mapper.AcidOeeMapper;
|
||||
import com.klp.pocket.acid.service.IAcidOeeService;
|
||||
import com.klp.pocket.acid.service.IKlptcm1ProStoppageService;
|
||||
import com.klp.pocket.common.service.ICoilQualityJudgeService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.*;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 酸轧线OEE Service实现类
|
||||
@@ -38,65 +29,53 @@ import java.util.Set;
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@DS("acid")
|
||||
@Service
|
||||
public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
|
||||
/** 次品判级集合(C/D系列) */
|
||||
private static final Set<String> CD_SERIES = new HashSet<>(
|
||||
Arrays.asList("C+", "C", "C-", "D+", "D", "D-")
|
||||
);
|
||||
|
||||
private final AcidOeeMasterMapper acidOeeMasterMapper;
|
||||
/** 酸轧成品库库区ID */
|
||||
private static final Long ACID_FINISHED_WAREHOUSE_ID = 1988150099140866050L;
|
||||
/** 固定理论节拍(min/吨) */
|
||||
private static final BigDecimal FIXED_IDEAL_CYCLE = BigDecimal.valueOf(0.47);
|
||||
private final AcidOeeMapper acidOeeMapper;
|
||||
private final IKlptcm1ProStoppageService stoppageService;
|
||||
|
||||
@Value("${oee.acid.coil-create-by}")
|
||||
private String acidCreateName;
|
||||
private final ICoilQualityJudgeService coilQualityJudgeService;
|
||||
|
||||
@Override
|
||||
public List<AcidOeeDailySummaryVo> getDailySummary(String startDate, String endDate) {
|
||||
// 1. 查询基础日汇总(产量、停机时间等)
|
||||
List<AcidOeeDailySummaryVo> summaries = acidOeeMasterMapper.selectDailySummary(
|
||||
startDate,
|
||||
endDate,
|
||||
acidCreateName
|
||||
);
|
||||
List<AcidOeeDailySummaryVo> summaries = acidOeeMapper.selectDailySummary(startDate, endDate);
|
||||
|
||||
if (summaries == null || summaries.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2. 查询停机事件,按日期聚合停机时间
|
||||
Map<String, Long> downtimeByDate = aggregateDowntimeByDate(startDate, endDate);
|
||||
|
||||
// 3. 查询产量明细,用于良品/次品判定
|
||||
Map<String, List<CoilInfo>> coilInfoByDate = getCoilNosByDate(startDate, endDate);
|
||||
|
||||
// 4. 先按天计算 dailyCycle = runTime/totalOutputTon,再取中位数作为理论节拍
|
||||
List<BigDecimal> dailyCycles = new ArrayList<>();
|
||||
// 4. 理论节拍:使用固定值0.47
|
||||
BigDecimal idealCycleTon = FIXED_IDEAL_CYCLE;
|
||||
|
||||
// 5. 填充每个日汇总的完整数据
|
||||
for (AcidOeeDailySummaryVo summary : summaries) {
|
||||
String statDate = summary.getStatDate();
|
||||
summary.setLineId("SY");
|
||||
summary.setLineName("酸轧线");
|
||||
|
||||
// 填充停机时间
|
||||
Long downtime = downtimeByDate.getOrDefault(statDate, 0L);
|
||||
summary.setDowntimeMin(downtime);
|
||||
|
||||
// 计算运转时间
|
||||
Long loadingTime = summary.getLoadingTimeMin() != null ? summary.getLoadingTimeMin() : 0L;
|
||||
Long runTime = Math.max(0, loadingTime - downtime);
|
||||
summary.setRunTimeMin(runTime);
|
||||
|
||||
BigDecimal totalOutputTon = summary.getTotalOutputTon();
|
||||
if (runTime > 0 && totalOutputTon != null && totalOutputTon.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal dailyCycle = BigDecimal.valueOf(runTime)
|
||||
.divide(totalOutputTon, 6, RoundingMode.HALF_UP);
|
||||
dailyCycles.add(dailyCycle);
|
||||
}
|
||||
}
|
||||
dailyCycles.sort(BigDecimal::compareTo);
|
||||
BigDecimal idealCycleTon = applyEightyPercent(median(dailyCycles));
|
||||
|
||||
// 5. 回填理论节拍、良品次品并计算派生指标
|
||||
for (AcidOeeDailySummaryVo summary : summaries) {
|
||||
String statDate = summary.getStatDate();
|
||||
if (idealCycleTon != null) {
|
||||
// 理论节拍:若尚未填充,则统一使用“优良日统计”得到的节拍
|
||||
if (summary.getIdealCycleTimeMinPerTon() == null && idealCycleTon != null) {
|
||||
summary.setIdealCycleTimeMinPerTon(idealCycleTon);
|
||||
}
|
||||
|
||||
@@ -105,19 +84,18 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
List<CoilInfo> coilInfos = coilInfoByDate.get(statDate);
|
||||
calculateQualityOutput(summary, coilInfos);
|
||||
} else {
|
||||
// 没有卷明细时,全部归待判级
|
||||
summary.setGoodOutputTon(BigDecimal.ZERO);
|
||||
summary.setGoodOutputCoil(0L);
|
||||
summary.setQualifiedOutputTon(BigDecimal.ZERO);
|
||||
summary.setQualifiedOutputCoil(0L);
|
||||
summary.setAbOutputTon(BigDecimal.ZERO);
|
||||
summary.setAbOutputCoil(0L);
|
||||
// 如果没有卷号,默认全部为良品(或根据业务规则处理)
|
||||
summary.setGoodOutputTon(summary.getTotalOutputTon());
|
||||
summary.setGoodOutputCoil(summary.getTotalOutputCoil());
|
||||
summary.setDefectOutputTon(BigDecimal.ZERO);
|
||||
summary.setDefectOutputCoil(0L);
|
||||
summary.setPendingOutputTon(summary.getTotalOutputTon());
|
||||
summary.setPendingOutputCoil(summary.getTotalOutputCoil());
|
||||
}
|
||||
|
||||
// 填充理论节拍(从回归数据或缓存获取,这里暂时留空,由调用方填充)
|
||||
// summary.setIdealCycleTimeMinPerTon(...);
|
||||
// summary.setIdealCycleTimeMinPerCoil(...);
|
||||
|
||||
// 计算派生指标
|
||||
calculateDerivedMetrics(summary);
|
||||
}
|
||||
|
||||
@@ -149,11 +127,7 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
@Override
|
||||
public AcidOeeIdealCycleVo getIdealCycle(String startDate, String endDate) {
|
||||
// 1) 取基础日汇总(产量、负荷时间等)
|
||||
List<AcidOeeDailySummaryVo> daily = acidOeeMasterMapper.selectDailySummary(
|
||||
startDate,
|
||||
endDate,
|
||||
acidCreateName
|
||||
);
|
||||
List<AcidOeeDailySummaryVo> daily = acidOeeMapper.selectDailySummary(startDate, endDate);
|
||||
AcidOeeIdealCycleVo rsp = new AcidOeeIdealCycleVo();
|
||||
rsp.setLineId("SY");
|
||||
rsp.setLineName("酸轧线");
|
||||
@@ -177,31 +151,23 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
d.setRunTimeMin(Math.max(0, loading - downtime));
|
||||
}
|
||||
|
||||
// 3) 理论节拍按“天维度”:每天(运转时间/总吨),再按日样本统计中位数用于展示
|
||||
List<BigDecimal> dailyCycles = new ArrayList<>();
|
||||
for (AcidOeeDailySummaryVo d : daily) {
|
||||
Long run = d.getRunTimeMin();
|
||||
BigDecimal ton = d.getTotalOutputTon();
|
||||
if (run == null || run <= 0 || ton == null || ton.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
continue;
|
||||
}
|
||||
dailyCycles.add(BigDecimal.valueOf(run).divide(ton, 6, RoundingMode.HALF_UP));
|
||||
}
|
||||
dailyCycles.sort(BigDecimal::compareTo);
|
||||
BigDecimal medianCycle = median(dailyCycles);
|
||||
|
||||
// 理论节拍:按“当天运转时间/当天总吨”逐日计算,接口返回前乘以80%
|
||||
BigDecimal idealCycle = applyEightyPercent(medianCycle);
|
||||
rsp.setIdealCycleTimeMinPerTon(idealCycle);
|
||||
// 展示字段保持为中位数
|
||||
// 3) 卷级节拍 = (END_DATE - START_DATE)/出口重量,计算中位数(用于展示,不用于OEE计算)
|
||||
List<BigDecimal> coilCycles = acidOeeMapper.selectCoilCycleMinPerTon(startDate, endDate);
|
||||
coilCycles.removeIf(c -> c == null || c.compareTo(BigDecimal.ZERO) <= 0);
|
||||
coilCycles.sort(BigDecimal::compareTo);
|
||||
BigDecimal medianCycle = median(coilCycles);
|
||||
|
||||
// 理论节拍使用固定值0.47(用于OEE计算)
|
||||
rsp.setIdealCycleTimeMinPerTon(FIXED_IDEAL_CYCLE);
|
||||
// 中位数理论节拍(用于展示)
|
||||
rsp.setMedianCycleTimeMinPerTon(medianCycle);
|
||||
// 样本天数:当前查询区间内有产量的自然日数量(与传入的日期范围一一对应)
|
||||
rsp.setSampleDays(daily.size());
|
||||
|
||||
// 4) 日粒度对比数据:理论耗时 vs 实际运转时间(用于前端展示"有效性")
|
||||
// 使用“中间50%样本平均理论节拍”计算理论耗时
|
||||
// 使用固定值0.47计算理论耗时
|
||||
List<AcidOeeIdealCycleVo.DailyComparePointVo> compare = new ArrayList<>();
|
||||
if (idealCycle != null) {
|
||||
if (FIXED_IDEAL_CYCLE != null) {
|
||||
for (AcidOeeDailySummaryVo d : daily) {
|
||||
BigDecimal ton = d.getTotalOutputTon();
|
||||
Long run = d.getRunTimeMin();
|
||||
@@ -209,7 +175,7 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
AcidOeeIdealCycleVo.DailyComparePointVo p = new AcidOeeIdealCycleVo.DailyComparePointVo();
|
||||
p.setStatDate(d.getStatDate());
|
||||
p.setActualRunTimeMin(run);
|
||||
p.setTheoreticalTimeMin(idealCycle.multiply(ton));
|
||||
p.setTheoreticalTimeMin(FIXED_IDEAL_CYCLE.multiply(ton));
|
||||
compare.add(p);
|
||||
}
|
||||
}
|
||||
@@ -304,25 +270,25 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
|
||||
Calendar cal = Calendar.getInstance();
|
||||
|
||||
|
||||
for (Klptcm1ProStoppageVo event : events) {
|
||||
if (event.getStartDate() == null || event.getDuration() == null || event.getDuration() <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Date eventStart = event.getStartDate();
|
||||
long durationSec = event.getDuration();
|
||||
long durationMin = (durationSec + 59) / 60; // 向上取整,避免丢失秒数
|
||||
|
||||
|
||||
// 计算停机结束时间
|
||||
cal.setTime(eventStart);
|
||||
cal.add(Calendar.SECOND, (int) durationSec);
|
||||
Date eventEnd = cal.getTime();
|
||||
|
||||
|
||||
// 如果停机事件在同一天,直接累加
|
||||
String startDateStr = dateFormat.format(eventStart);
|
||||
String endDateStr = dateFormat.format(eventEnd);
|
||||
|
||||
|
||||
if (startDateStr.equals(endDateStr)) {
|
||||
// 同一天,直接累加
|
||||
downtimeMap.merge(startDateStr, durationMin, Long::sum);
|
||||
@@ -334,23 +300,23 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
Date dayStart = cal.getTime();
|
||||
|
||||
|
||||
Date currentDayStart = dayStart;
|
||||
|
||||
|
||||
while (currentDayStart.before(eventEnd)) {
|
||||
cal.setTime(currentDayStart);
|
||||
cal.add(Calendar.DAY_OF_MONTH, 1);
|
||||
Date nextDayStart = cal.getTime();
|
||||
|
||||
|
||||
// 计算当前天的停机分钟数
|
||||
Date dayEnd = nextDayStart.before(eventEnd) ? nextDayStart : eventEnd;
|
||||
long dayMinutes = Math.max(0, (dayEnd.getTime() - Math.max(currentDayStart.getTime(), eventStart.getTime())) / (1000 * 60));
|
||||
|
||||
|
||||
if (dayMinutes > 0) {
|
||||
String dateKey = dateFormat.format(currentDayStart);
|
||||
downtimeMap.merge(dateKey, dayMinutes, Long::sum);
|
||||
}
|
||||
|
||||
|
||||
currentDayStart = nextDayStart;
|
||||
}
|
||||
}
|
||||
@@ -363,18 +329,13 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
* 获取每日的钢卷号和重量(用于良品/次品判定)
|
||||
*/
|
||||
private Map<String, List<CoilInfo>> getCoilNosByDate(String startDate, String endDate) {
|
||||
|
||||
List<AcidOeeCoilInfoByDateVo> coilInfoList = acidOeeMasterMapper.selectCoilInfoByDate(
|
||||
startDate,
|
||||
endDate,
|
||||
acidCreateName
|
||||
);
|
||||
List<AcidOeeMapper.CoilInfoByDate> coilInfoList = acidOeeMapper.selectCoilInfoByDate(startDate, endDate);
|
||||
Map<String, List<CoilInfo>> result = new HashMap<>();
|
||||
|
||||
for (AcidOeeCoilInfoByDateVo info : coilInfoList) {
|
||||
for (AcidOeeMapper.CoilInfoByDate info : coilInfoList) {
|
||||
String date = info.getStatDate();
|
||||
result.computeIfAbsent(date, k -> new ArrayList<>())
|
||||
.add(new CoilInfo(info.getCoilNo(), info.getWeight(), info.getQualityStatus()));
|
||||
.add(new CoilInfo(info.getCoilNo(), info.getWeight()));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -384,64 +345,50 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
* 卷号信息内部类
|
||||
*/
|
||||
private static class CoilInfo {
|
||||
final String coilNo;
|
||||
final BigDecimal weight;
|
||||
final String qualityStatus;
|
||||
|
||||
CoilInfo(String coilNo, BigDecimal weight, String qualityStatus) {
|
||||
CoilInfo(String coilNo, BigDecimal weight) {
|
||||
this.coilNo = coilNo;
|
||||
this.weight = weight;
|
||||
this.qualityStatus = qualityStatus;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算质量细分产量:A良品、B合格品、C/D次品、O待判级。
|
||||
* 计算良品/次品产量
|
||||
*/
|
||||
private void calculateQualityOutput(AcidOeeDailySummaryVo summary, List<CoilInfo> coilInfos) {
|
||||
BigDecimal aTon = BigDecimal.ZERO;
|
||||
long aCoil = 0L;
|
||||
BigDecimal bTon = BigDecimal.ZERO;
|
||||
long bCoil = 0L;
|
||||
BigDecimal cdTon = BigDecimal.ZERO;
|
||||
long cdCoil = 0L;
|
||||
BigDecimal oTon = BigDecimal.ZERO;
|
||||
long oCoil = 0L;
|
||||
BigDecimal goodTon = BigDecimal.ZERO;
|
||||
long goodCoil = 0L;
|
||||
BigDecimal defectTon = BigDecimal.ZERO;
|
||||
long defectCoil = 0L;
|
||||
|
||||
for (CoilInfo coilInfo : coilInfos) {
|
||||
String coilNo = coilInfo.coilNo;
|
||||
BigDecimal coilWeight = coilInfo.weight != null ? coilInfo.weight : BigDecimal.ZERO;
|
||||
String qualityStatus = StringUtils.upperCase(StringUtils.trim(coilInfo.qualityStatus));
|
||||
|
||||
if (StringUtils.isBlank(qualityStatus) || "O".equals(qualityStatus)) {
|
||||
oTon = oTon.add(coilWeight);
|
||||
oCoil++;
|
||||
// 通过WMS判定良品/次品
|
||||
Boolean isScrap = coilQualityJudgeService.isScrap(ACID_FINISHED_WAREHOUSE_ID, coilNo);
|
||||
if (isScrap == null) {
|
||||
// 匹配不到,忽略不计
|
||||
continue;
|
||||
}
|
||||
|
||||
if (qualityStatus.startsWith("A")) {
|
||||
aTon = aTon.add(coilWeight);
|
||||
aCoil++;
|
||||
} else if (qualityStatus.startsWith("B")) {
|
||||
bTon = bTon.add(coilWeight);
|
||||
bCoil++;
|
||||
} else if (CD_SERIES.contains(qualityStatus)) {
|
||||
cdTon = cdTon.add(coilWeight);
|
||||
cdCoil++;
|
||||
if (Boolean.TRUE.equals(isScrap)) {
|
||||
// 次品
|
||||
defectTon = defectTon.add(coilWeight);
|
||||
defectCoil++;
|
||||
} else {
|
||||
// 未识别等级统一归待判级
|
||||
oTon = oTon.add(coilWeight);
|
||||
oCoil++;
|
||||
// 良品
|
||||
goodTon = goodTon.add(coilWeight);
|
||||
goodCoil++;
|
||||
}
|
||||
}
|
||||
|
||||
summary.setGoodOutputTon(aTon);
|
||||
summary.setGoodOutputCoil(aCoil);
|
||||
summary.setQualifiedOutputTon(bTon);
|
||||
summary.setQualifiedOutputCoil(bCoil);
|
||||
summary.setAbOutputTon(aTon.add(bTon));
|
||||
summary.setAbOutputCoil(aCoil + bCoil);
|
||||
summary.setDefectOutputTon(cdTon);
|
||||
summary.setDefectOutputCoil(cdCoil);
|
||||
summary.setPendingOutputTon(oTon);
|
||||
summary.setPendingOutputCoil(oCoil);
|
||||
summary.setGoodOutputTon(goodTon);
|
||||
summary.setGoodOutputCoil(goodCoil);
|
||||
summary.setDefectOutputTon(defectTon);
|
||||
summary.setDefectOutputCoil(defectCoil);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -459,7 +406,6 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
}
|
||||
|
||||
// 性能稼动率(吨维度)
|
||||
// 口径:理论节拍单位为 min/吨 时,性能稼动率 = (理论节拍 × 实际产量) / 实际运转时间 × 100
|
||||
Long runTime = summary.getRunTimeMin() != null ? summary.getRunTimeMin() : 0L;
|
||||
BigDecimal idealCycleTon = summary.getIdealCycleTimeMinPerTon();
|
||||
BigDecimal totalOutputTon = summary.getTotalOutputTon();
|
||||
@@ -469,14 +415,12 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
&& totalOutputTon != null
|
||||
&& totalOutputTon.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal idealTime = idealCycleTon.multiply(totalOutputTon);
|
||||
BigDecimal performanceTon = idealTime
|
||||
.divide(BigDecimal.valueOf(runTime), 4, RoundingMode.HALF_UP)
|
||||
BigDecimal performanceTon = idealTime.divide(BigDecimal.valueOf(runTime), 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
summary.setPerformanceTon(performanceTon);
|
||||
}
|
||||
|
||||
// 性能稼动率(卷维度)
|
||||
// 口径:理论节拍单位为 min/卷 时,性能稼动率 = (理论节拍 × 实际产量) / 实际运转时间 × 100
|
||||
Long totalOutputCoil = summary.getTotalOutputCoil() != null ? summary.getTotalOutputCoil() : 0L;
|
||||
BigDecimal idealCycleCoil = summary.getIdealCycleTimeMinPerCoil();
|
||||
if (runTime > 0
|
||||
@@ -484,35 +428,17 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
&& idealCycleCoil.compareTo(BigDecimal.ZERO) > 0
|
||||
&& totalOutputCoil > 0) {
|
||||
BigDecimal idealTime = idealCycleCoil.multiply(BigDecimal.valueOf(totalOutputCoil));
|
||||
BigDecimal performanceCoil = idealTime
|
||||
.divide(BigDecimal.valueOf(runTime), 4, RoundingMode.HALF_UP)
|
||||
BigDecimal performanceCoil = idealTime.divide(BigDecimal.valueOf(runTime), 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
summary.setPerformanceCoil(performanceCoil);
|
||||
}
|
||||
|
||||
// 质量细分比率
|
||||
// 良品率
|
||||
if (totalOutputTon != null && totalOutputTon.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal aTon = summary.getGoodOutputTon() != null ? summary.getGoodOutputTon() : BigDecimal.ZERO;
|
||||
BigDecimal bTon = summary.getQualifiedOutputTon() != null ? summary.getQualifiedOutputTon() : BigDecimal.ZERO;
|
||||
BigDecimal cdTon = summary.getDefectOutputTon() != null ? summary.getDefectOutputTon() : BigDecimal.ZERO;
|
||||
BigDecimal oTon = summary.getPendingOutputTon() != null ? summary.getPendingOutputTon() : BigDecimal.ZERO;
|
||||
|
||||
BigDecimal quality = aTon.divide(totalOutputTon, 4, RoundingMode.HALF_UP)
|
||||
BigDecimal goodOutputTon = summary.getGoodOutputTon() != null ? summary.getGoodOutputTon() : BigDecimal.ZERO;
|
||||
BigDecimal quality = goodOutputTon.divide(totalOutputTon, 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
summary.setQuality(quality);
|
||||
|
||||
BigDecimal qualifiedRate = aTon.add(bTon)
|
||||
.divide(totalOutputTon, 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
summary.setQualifiedRate(qualifiedRate);
|
||||
|
||||
BigDecimal defectRate = cdTon.divide(totalOutputTon, 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
summary.setDefectRate(defectRate);
|
||||
|
||||
BigDecimal pendingRate = oTon.divide(totalOutputTon, 4, RoundingMode.HALF_UP)
|
||||
.multiply(BigDecimal.valueOf(100));
|
||||
summary.setPendingRate(pendingRate);
|
||||
}
|
||||
|
||||
// OEE(以吨维度为主)
|
||||
@@ -537,15 +463,6 @@ public class AcidOeeServiceImpl implements IAcidOeeService {
|
||||
return a.add(b).divide(BigDecimal.valueOf(2), 6, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/** 理论节拍返回前按业务口径乘以70% */
|
||||
private BigDecimal applyEightyPercent(BigDecimal cycle) {
|
||||
if (cycle == null) {
|
||||
return null;
|
||||
}
|
||||
return cycle.multiply(BigDecimal.valueOf(0.7)).setScale(6, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 解析日期字符串为Date对象
|
||||
*/
|
||||
|
||||
@@ -18,7 +18,6 @@ public class CrmPdiPlan {
|
||||
|
||||
private Integer seqid;
|
||||
private String coilid;
|
||||
private String enterCoilNo;
|
||||
private Integer dummyCoilFlag;
|
||||
private String status;
|
||||
private String planid;
|
||||
|
||||
@@ -19,7 +19,6 @@ public class CrmPdoExcoil {
|
||||
|
||||
private String exitMatId;
|
||||
private String entryMatId;
|
||||
private String enterCoilNo;
|
||||
private Integer subId;
|
||||
private Double startPosition;
|
||||
private Double endPosition;
|
||||
|
||||
@@ -15,7 +15,6 @@ public class CrmPdiPlanBo extends BaseEntity {
|
||||
|
||||
private Integer seqid;
|
||||
private String coilid;
|
||||
private String enterCoilNo;
|
||||
private Integer dummyCoilFlag;
|
||||
private String status;
|
||||
private String planid;
|
||||
|
||||
@@ -15,7 +15,6 @@ public class CrmPdoExcoilBo extends BaseEntity {
|
||||
|
||||
private String exitMatId;
|
||||
private String entryMatId;
|
||||
private String enterCoilNo;
|
||||
private Integer subId;
|
||||
private Double startPosition;
|
||||
private Double endPosition;
|
||||
|
||||
@@ -12,7 +12,6 @@ public class CrmPdiPlanVo {
|
||||
|
||||
private Integer seqid;
|
||||
private String coilid;
|
||||
private String enterCoilNo;
|
||||
private Integer dummyCoilFlag;
|
||||
private String status;
|
||||
private String planid;
|
||||
|
||||
@@ -12,7 +12,6 @@ public class CrmPdoExcoilVo {
|
||||
|
||||
private String exitMatId;
|
||||
private String entryMatId;
|
||||
private String enterCoilNo;
|
||||
private Integer subId;
|
||||
private Double startPosition;
|
||||
private Double endPosition;
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.klp.pocket.galvanize1.mapper;
|
||||
|
||||
import com.baomidou.dynamic.datasource.annotation.DS;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeCoilInfoByDateVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
@DS("master")
|
||||
public interface GalvanizeOeeMasterMapper {
|
||||
|
||||
List<AcidOeeDailySummaryVo> selectDailySummary(@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate,
|
||||
@Param("createBy") String createBy);
|
||||
|
||||
List<AcidOeeCoilInfoByDateVo> selectCoilInfoByDate(@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate,
|
||||
@Param("createBy") String createBy);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.klp.pocket.galvanize1.service;
|
||||
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeIdealCycleVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeLoss7Vo;
|
||||
import com.klp.pocket.acid.domain.vo.Klptcm1ProStoppageVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface IGalvanizeOeeService {
|
||||
|
||||
List<AcidOeeDailySummaryVo> getDailySummary(String startDate, String endDate);
|
||||
|
||||
List<Klptcm1ProStoppageVo> getStoppageEvents(String startDate, String endDate);
|
||||
|
||||
List<AcidOeeLoss7Vo> getLoss7Summary(String startDate, String endDate);
|
||||
|
||||
AcidOeeIdealCycleVo getIdealCycle(String startDate, String endDate);
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@DS("galvanize1")
|
||||
@@ -45,11 +46,11 @@ public class CrmPdiPlanServiceImpl implements ICrmPdiPlanService {
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<CrmPdiPlan> buildQueryWrapper(CrmPdiPlanBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<CrmPdiPlan> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getId() != null, CrmPdiPlan::getId, bo.getId());
|
||||
lqw.eq(bo.getSeqid() != null, CrmPdiPlan::getSeqid, bo.getSeqid());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getCoilid()), CrmPdiPlan::getCoilid, bo.getCoilid());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getEnterCoilNo()), CrmPdiPlan::getEnterCoilNo, bo.getEnterCoilNo());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), CrmPdiPlan::getStatus, bo.getStatus());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getPlanid()), CrmPdiPlan::getPlanid, bo.getPlanid());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getPlanType()), CrmPdiPlan::getPlanType, bo.getPlanType());
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@DS("galvanize1")
|
||||
@@ -45,15 +46,15 @@ public class CrmPdoExcoilServiceImpl implements ICrmPdoExcoilService {
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<CrmPdoExcoil> buildQueryWrapper(CrmPdoExcoilBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<CrmPdoExcoil> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getId() != null, CrmPdoExcoil::getId, bo.getId());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getExitMatId()), CrmPdoExcoil::getExitMatId, bo.getExitMatId());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getEntryMatId()), CrmPdoExcoil::getEntryMatId, bo.getEntryMatId());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getEnterCoilNo()), CrmPdoExcoil::getEnterCoilNo, bo.getEnterCoilNo());
|
||||
lqw.eq(bo.getPlanId() != null, CrmPdoExcoil::getPlanId, bo.getPlanId());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), CrmPdoExcoil::getStatus, bo.getStatus());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getUnitCode()), CrmPdoExcoil::getUnitCode, bo.getUnitCode());
|
||||
lqw.orderByDesc(CrmPdoExcoil::getCreateTime);
|
||||
lqw.orderByDesc(CrmPdoExcoil::getId);
|
||||
return lqw;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,274 +0,0 @@
|
||||
package com.klp.pocket.galvanize1.service.impl;
|
||||
|
||||
import com.klp.common.utils.StringUtils;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeCoilInfoByDateVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeIdealCycleVo;
|
||||
import com.klp.pocket.acid.domain.vo.AcidOeeLoss7Vo;
|
||||
import com.klp.pocket.acid.domain.vo.Klptcm1ProStoppageVo;
|
||||
import com.klp.pocket.galvanize1.domain.bo.ProStoppageBo;
|
||||
import com.klp.pocket.galvanize1.domain.vo.ProStoppageVo;
|
||||
import com.klp.pocket.galvanize1.mapper.GalvanizeOeeMasterMapper;
|
||||
import com.klp.pocket.galvanize1.service.IGalvanizeOeeService;
|
||||
import com.klp.pocket.galvanize1.service.IProStoppageService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class GalvanizeOeeServiceImpl implements IGalvanizeOeeService {
|
||||
|
||||
private static final Set<String> CD_SERIES = new HashSet<>(Arrays.asList("C+", "C", "C-", "D+", "D", "D-"));
|
||||
|
||||
private final GalvanizeOeeMasterMapper galvanizeOeeMasterMapper;
|
||||
private final IProStoppageService proStoppageService;
|
||||
|
||||
@Value("${oee.galvanize1.coil-create-by:duxinkuguan}")
|
||||
private String galvanizeCreateBy;
|
||||
|
||||
@Override
|
||||
public List<AcidOeeDailySummaryVo> getDailySummary(String startDate, String endDate) {
|
||||
List<AcidOeeDailySummaryVo> summaries = galvanizeOeeMasterMapper.selectDailySummary(startDate, endDate, galvanizeCreateBy);
|
||||
if (summaries == null || summaries.isEmpty()) return Collections.emptyList();
|
||||
|
||||
Map<String, Long> downtimeByDate = aggregateDowntimeByDate(startDate, endDate);
|
||||
Map<String, List<CoilInfo>> coilInfoByDate = getCoilNosByDate(startDate, endDate);
|
||||
|
||||
List<BigDecimal> dailyCycles = new ArrayList<>();
|
||||
for (AcidOeeDailySummaryVo s : summaries) {
|
||||
s.setLineId("DX1");
|
||||
s.setLineName("镀锌一线");
|
||||
Long downtime = downtimeByDate.getOrDefault(s.getStatDate(), 0L);
|
||||
s.setDowntimeMin(downtime);
|
||||
Long loading = s.getLoadingTimeMin() == null ? 0L : s.getLoadingTimeMin();
|
||||
Long run = Math.max(0, loading - downtime);
|
||||
s.setRunTimeMin(run);
|
||||
BigDecimal ton = s.getTotalOutputTon();
|
||||
if (run > 0 && ton != null && ton.compareTo(BigDecimal.ZERO) > 0) {
|
||||
dailyCycles.add(BigDecimal.valueOf(run).divide(ton, 6, RoundingMode.HALF_UP));
|
||||
}
|
||||
}
|
||||
dailyCycles.sort(BigDecimal::compareTo);
|
||||
BigDecimal ideal = applyEightyPercent(median(dailyCycles));
|
||||
|
||||
for (AcidOeeDailySummaryVo s : summaries) {
|
||||
if (ideal != null) s.setIdealCycleTimeMinPerTon(ideal);
|
||||
List<CoilInfo> coilInfos = coilInfoByDate.get(s.getStatDate());
|
||||
if (coilInfos != null) {
|
||||
calculateQualityOutput(s, coilInfos);
|
||||
} else {
|
||||
s.setGoodOutputTon(BigDecimal.ZERO);
|
||||
s.setGoodOutputCoil(0L);
|
||||
s.setQualifiedOutputTon(BigDecimal.ZERO);
|
||||
s.setQualifiedOutputCoil(0L);
|
||||
s.setAbOutputTon(BigDecimal.ZERO);
|
||||
s.setAbOutputCoil(0L);
|
||||
s.setDefectOutputTon(BigDecimal.ZERO);
|
||||
s.setDefectOutputCoil(0L);
|
||||
s.setPendingOutputTon(s.getTotalOutputTon());
|
||||
s.setPendingOutputCoil(s.getTotalOutputCoil());
|
||||
}
|
||||
calculateDerivedMetrics(s);
|
||||
}
|
||||
return summaries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Klptcm1ProStoppageVo> getStoppageEvents(String startDate, String endDate) {
|
||||
ProStoppageBo bo = new ProStoppageBo();
|
||||
bo.setStartDate(parseDate(startDate));
|
||||
bo.setEndDate(parseDate(endDate));
|
||||
List<ProStoppageVo> list = proStoppageService.queryList(bo);
|
||||
List<Klptcm1ProStoppageVo> out = new ArrayList<>();
|
||||
for (ProStoppageVo p : list) {
|
||||
Klptcm1ProStoppageVo v = new Klptcm1ProStoppageVo();
|
||||
v.setStopid(p.getStopid());
|
||||
v.setShift(p.getShift());
|
||||
v.setCrew(p.getCrew());
|
||||
v.setArea(p.getArea());
|
||||
v.setUnit(p.getUnit());
|
||||
v.setSeton(p.getSeton());
|
||||
v.setStartDate(p.getStartDate());
|
||||
v.setEndDate(p.getEndDate());
|
||||
v.setRemark(p.getRemark());
|
||||
v.setStopType(p.getStopType());
|
||||
long durationSec = p.getDuration() == null ? 0L : Math.round(p.getDuration());
|
||||
v.setDuration(durationSec);
|
||||
out.add(v);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AcidOeeLoss7Vo> getLoss7Summary(String startDate, String endDate) {
|
||||
List<Klptcm1ProStoppageVo> events = getStoppageEvents(startDate, endDate);
|
||||
if (events.isEmpty()) return Collections.emptyList();
|
||||
Map<String, long[]> m = new HashMap<>();
|
||||
long total = 0L;
|
||||
for (Klptcm1ProStoppageVo e : events) {
|
||||
String k = StringUtils.isBlank(e.getStopType()) ? "未分类" : e.getStopType();
|
||||
long min = Math.max(1, (e.getDuration() == null ? 0L : e.getDuration()) / 60);
|
||||
if (min <= 0) continue;
|
||||
m.computeIfAbsent(k, x -> new long[2]);
|
||||
m.get(k)[0] += min;
|
||||
m.get(k)[1] += 1;
|
||||
total += min;
|
||||
}
|
||||
List<AcidOeeLoss7Vo> out = new ArrayList<>();
|
||||
for (Map.Entry<String, long[]> en : m.entrySet()) {
|
||||
AcidOeeLoss7Vo vo = new AcidOeeLoss7Vo();
|
||||
vo.setLossCategoryCode(en.getKey());
|
||||
vo.setLossCategoryName(en.getKey());
|
||||
vo.setLossTimeMin(en.getValue()[0]);
|
||||
vo.setCount((int) en.getValue()[1]);
|
||||
vo.setAvgDurationMin(BigDecimal.valueOf(en.getValue()[0]).divide(BigDecimal.valueOf(Math.max(1, en.getValue()[1])), 2, RoundingMode.HALF_UP));
|
||||
vo.setLossTimeRate(total == 0 ? BigDecimal.ZERO : BigDecimal.valueOf(en.getValue()[0]).divide(BigDecimal.valueOf(total), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
|
||||
out.add(vo);
|
||||
}
|
||||
out.sort(Comparator.comparingLong(AcidOeeLoss7Vo::getLossTimeMin).reversed());
|
||||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AcidOeeIdealCycleVo getIdealCycle(String startDate, String endDate) {
|
||||
List<AcidOeeDailySummaryVo> daily = getDailySummary(startDate, endDate);
|
||||
AcidOeeIdealCycleVo rsp = new AcidOeeIdealCycleVo();
|
||||
rsp.setLineId("DX1");
|
||||
rsp.setLineName("镀锌一线");
|
||||
rsp.setStartDate(startDate);
|
||||
rsp.setEndDate(endDate);
|
||||
if (daily.isEmpty()) {
|
||||
rsp.setDailyComparePoints(Collections.emptyList());
|
||||
rsp.setSampleDays(0);
|
||||
return rsp;
|
||||
}
|
||||
List<BigDecimal> dailyCycles = new ArrayList<>();
|
||||
List<AcidOeeIdealCycleVo.DailyComparePointVo> points = new ArrayList<>();
|
||||
for (AcidOeeDailySummaryVo d : daily) {
|
||||
if (d.getRunTimeMin() == null || d.getRunTimeMin() <= 0 || d.getTotalOutputTon() == null || d.getTotalOutputTon().compareTo(BigDecimal.ZERO) <= 0) continue;
|
||||
BigDecimal c = BigDecimal.valueOf(d.getRunTimeMin()).divide(d.getTotalOutputTon(), 6, RoundingMode.HALF_UP);
|
||||
dailyCycles.add(c);
|
||||
}
|
||||
dailyCycles.sort(BigDecimal::compareTo);
|
||||
BigDecimal med = median(dailyCycles);
|
||||
BigDecimal ideal = applyEightyPercent(med);
|
||||
for (AcidOeeDailySummaryVo d : daily) {
|
||||
if (ideal == null || d.getTotalOutputTon() == null || d.getRunTimeMin() == null) continue;
|
||||
AcidOeeIdealCycleVo.DailyComparePointVo p = new AcidOeeIdealCycleVo.DailyComparePointVo();
|
||||
p.setStatDate(d.getStatDate());
|
||||
p.setActualRunTimeMin(d.getRunTimeMin());
|
||||
p.setTheoreticalTimeMin(ideal.multiply(d.getTotalOutputTon()));
|
||||
points.add(p);
|
||||
}
|
||||
rsp.setIdealCycleTimeMinPerTon(ideal);
|
||||
rsp.setMedianCycleTimeMinPerTon(med);
|
||||
rsp.setSampleDays(daily.size());
|
||||
rsp.setDailyComparePoints(points);
|
||||
return rsp;
|
||||
}
|
||||
|
||||
private Map<String, List<CoilInfo>> getCoilNosByDate(String startDate, String endDate) {
|
||||
List<AcidOeeCoilInfoByDateVo> list = galvanizeOeeMasterMapper.selectCoilInfoByDate(startDate, endDate, galvanizeCreateBy);
|
||||
Map<String, List<CoilInfo>> map = new HashMap<>();
|
||||
for (AcidOeeCoilInfoByDateVo i : list) {
|
||||
map.computeIfAbsent(i.getStatDate(), k -> new ArrayList<>()).add(new CoilInfo(i.getWeight(), i.getQualityStatus()));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private void calculateQualityOutput(AcidOeeDailySummaryVo summary, List<CoilInfo> coilInfos) {
|
||||
BigDecimal aTon = BigDecimal.ZERO, bTon = BigDecimal.ZERO, cdTon = BigDecimal.ZERO, oTon = BigDecimal.ZERO;
|
||||
long a = 0, b = 0, cd = 0, o = 0;
|
||||
for (CoilInfo c : coilInfos) {
|
||||
BigDecimal w = c.weight == null ? BigDecimal.ZERO : c.weight;
|
||||
String q = StringUtils.upperCase(StringUtils.trim(c.qualityStatus));
|
||||
if (StringUtils.isBlank(q) || "O".equals(q)) {
|
||||
oTon = oTon.add(w); o++; continue;
|
||||
}
|
||||
if (q.startsWith("A")) { aTon = aTon.add(w); a++; }
|
||||
else if (q.startsWith("B")) { bTon = bTon.add(w); b++; }
|
||||
else if (CD_SERIES.contains(q)) { cdTon = cdTon.add(w); cd++; }
|
||||
else { oTon = oTon.add(w); o++; }
|
||||
}
|
||||
summary.setGoodOutputTon(aTon); summary.setGoodOutputCoil(a);
|
||||
summary.setQualifiedOutputTon(bTon); summary.setQualifiedOutputCoil(b);
|
||||
summary.setAbOutputTon(aTon.add(bTon)); summary.setAbOutputCoil(a + b);
|
||||
summary.setDefectOutputTon(cdTon); summary.setDefectOutputCoil(cd);
|
||||
summary.setPendingOutputTon(oTon); summary.setPendingOutputCoil(o);
|
||||
}
|
||||
|
||||
private void calculateDerivedMetrics(AcidOeeDailySummaryVo s) {
|
||||
long loading = s.getLoadingTimeMin() == null ? 0 : s.getLoadingTimeMin();
|
||||
long downtime = s.getDowntimeMin() == null ? 0 : s.getDowntimeMin();
|
||||
if (loading > 0) s.setAvailability(BigDecimal.valueOf(loading - downtime).divide(BigDecimal.valueOf(loading), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
|
||||
long run = s.getRunTimeMin() == null ? 0 : s.getRunTimeMin();
|
||||
BigDecimal ideal = s.getIdealCycleTimeMinPerTon();
|
||||
BigDecimal ton = s.getTotalOutputTon();
|
||||
if (run > 0 && ideal != null && ideal.compareTo(BigDecimal.ZERO) > 0 && ton != null && ton.compareTo(BigDecimal.ZERO) > 0) {
|
||||
s.setPerformanceTon(ideal.multiply(ton).divide(BigDecimal.valueOf(run), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
|
||||
}
|
||||
if (ton != null && ton.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal a = Optional.ofNullable(s.getGoodOutputTon()).orElse(BigDecimal.ZERO);
|
||||
BigDecimal b = Optional.ofNullable(s.getQualifiedOutputTon()).orElse(BigDecimal.ZERO);
|
||||
BigDecimal cd = Optional.ofNullable(s.getDefectOutputTon()).orElse(BigDecimal.ZERO);
|
||||
BigDecimal o = Optional.ofNullable(s.getPendingOutputTon()).orElse(BigDecimal.ZERO);
|
||||
s.setQuality(a.divide(ton, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
|
||||
s.setQualifiedRate(a.add(b).divide(ton, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
|
||||
s.setDefectRate(cd.divide(ton, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
|
||||
s.setPendingRate(o.divide(ton, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
|
||||
}
|
||||
if (s.getAvailability() != null && s.getPerformanceTon() != null && s.getQuality() != null) {
|
||||
s.setOee(s.getAvailability().multiply(s.getPerformanceTon()).multiply(s.getQuality()).divide(BigDecimal.valueOf(10000), 4, RoundingMode.HALF_UP));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Long> aggregateDowntimeByDate(String startDate, String endDate) {
|
||||
List<Klptcm1ProStoppageVo> events = getStoppageEvents(startDate, endDate);
|
||||
Map<String, Long> map = new HashMap<>();
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
||||
for (Klptcm1ProStoppageVo e : events) {
|
||||
if (e.getStartDate() == null || e.getDuration() == null || e.getDuration() <= 0) continue;
|
||||
long min = Math.max(1, (e.getDuration() + 59) / 60);
|
||||
map.merge(df.format(e.getStartDate()), min, Long::sum);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private BigDecimal median(List<BigDecimal> values) {
|
||||
if (values == null || values.isEmpty()) return null;
|
||||
int n = values.size();
|
||||
if (n % 2 == 1) return values.get(n / 2);
|
||||
return values.get(n / 2 - 1).add(values.get(n / 2)).divide(BigDecimal.valueOf(2), 6, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private BigDecimal applyEightyPercent(BigDecimal v) {
|
||||
return v == null ? null : v.multiply(BigDecimal.valueOf(0.7)).setScale(6, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private Date parseDate(String dateStr) {
|
||||
if (StringUtils.isBlank(dateStr)) return null;
|
||||
try {
|
||||
return new SimpleDateFormat("yyyy-MM-dd").parse(dateStr);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CoilInfo {
|
||||
final BigDecimal weight;
|
||||
final String qualityStatus;
|
||||
|
||||
CoilInfo(BigDecimal weight, String qualityStatus) {
|
||||
this.weight = weight;
|
||||
this.qualityStatus = qualityStatus;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.klp.pocket.galvanize1.mapper.GalvanizeOeeMasterMapper">
|
||||
|
||||
<resultMap id="AcidOeeDailySummaryResultMap" type="com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo">
|
||||
<result column="stat_date" property="statDate" jdbcType="VARCHAR"/>
|
||||
<result column="line_id" property="lineId" jdbcType="VARCHAR"/>
|
||||
<result column="line_name" property="lineName" jdbcType="VARCHAR"/>
|
||||
<result column="planned_time_min" property="plannedTimeMin" jdbcType="BIGINT"/>
|
||||
<result column="planned_downtime_min" property="plannedDowntimeMin" jdbcType="BIGINT"/>
|
||||
<result column="loading_time_min" property="loadingTimeMin" jdbcType="BIGINT"/>
|
||||
<result column="downtime_min" property="downtimeMin" jdbcType="BIGINT"/>
|
||||
<result column="run_time_min" property="runTimeMin" jdbcType="BIGINT"/>
|
||||
<result column="total_output_ton" property="totalOutputTon" jdbcType="DECIMAL"/>
|
||||
<result column="total_output_coil" property="totalOutputCoil" jdbcType="BIGINT"/>
|
||||
<result column="good_output_ton" property="goodOutputTon" jdbcType="DECIMAL"/>
|
||||
<result column="good_output_coil" property="goodOutputCoil" jdbcType="BIGINT"/>
|
||||
<result column="defect_output_ton" property="defectOutputTon" jdbcType="DECIMAL"/>
|
||||
<result column="defect_output_coil" property="defectOutputCoil" jdbcType="BIGINT"/>
|
||||
<result column="ideal_cycle_time_min_per_ton" property="idealCycleTimeMinPerTon" jdbcType="DECIMAL"/>
|
||||
<result column="ideal_cycle_time_min_per_coil" property="idealCycleTimeMinPerCoil" jdbcType="DECIMAL"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectDailySummary" resultMap="AcidOeeDailySummaryResultMap">
|
||||
SELECT
|
||||
DATE_FORMAT(mc.create_time, '%Y-%m-%d') AS stat_date,
|
||||
'DX1' AS line_id,
|
||||
'镀锌一线' AS line_name,
|
||||
1440 AS planned_time_min,
|
||||
0 AS planned_downtime_min,
|
||||
1440 AS loading_time_min,
|
||||
0 AS downtime_min,
|
||||
COALESCE(SUM(mc.net_weight / 1000), 0) AS total_output_ton,
|
||||
COUNT(*) AS total_output_coil,
|
||||
0 AS good_output_ton,
|
||||
0 AS good_output_coil,
|
||||
0 AS defect_output_ton,
|
||||
0 AS defect_output_coil,
|
||||
NULL AS ideal_cycle_time_min_per_ton,
|
||||
NULL AS ideal_cycle_time_min_per_coil
|
||||
FROM wms_material_coil mc
|
||||
WHERE DATE(mc.create_time) BETWEEN #{startDate} AND #{endDate}
|
||||
AND mc.create_by = #{createBy}
|
||||
AND mc.del_flag = 0
|
||||
AND mc.data_type = 1
|
||||
AND mc.current_coil_no IS NOT NULL
|
||||
AND mc.net_weight IS NOT NULL
|
||||
AND mc.net_weight > 0
|
||||
GROUP BY DATE_FORMAT(mc.create_time, '%Y-%m-%d')
|
||||
ORDER BY stat_date ASC
|
||||
</select>
|
||||
|
||||
<select id="selectCoilInfoByDate" resultType="com.klp.pocket.acid.domain.vo.AcidOeeCoilInfoByDateVo">
|
||||
SELECT
|
||||
DATE_FORMAT(mc.create_time, '%Y-%m-%d') AS statDate,
|
||||
mc.current_coil_no AS coilNo,
|
||||
(mc.net_weight / 1000) AS weight,
|
||||
mc.quality_status AS qualityStatus
|
||||
FROM wms_material_coil mc
|
||||
WHERE DATE(mc.create_time) BETWEEN #{startDate} AND #{endDate}
|
||||
AND mc.create_by = #{createBy}
|
||||
AND mc.del_flag = 0
|
||||
AND mc.data_type = 1
|
||||
AND mc.current_coil_no IS NOT NULL
|
||||
AND mc.net_weight IS NOT NULL
|
||||
AND mc.net_weight > 0
|
||||
ORDER BY mc.create_time ASC, mc.current_coil_no ASC
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.klp.pocket.acid.mapper.AcidOeeAcidMapper">
|
||||
|
||||
<!-- 查询卷级生产节拍(min/吨):(END_DATE - START_DATE)/EXIT_WEIGHT -->
|
||||
<select id="selectCoilCycleMinPerTon" resultType="java.math.BigDecimal">
|
||||
SELECT
|
||||
TIMESTAMPDIFF(MINUTE, e.START_DATE, e.END_DATE) / e.EXIT_WEIGHT AS cycle_min_per_ton
|
||||
FROM klptcm1_pdo_excoil e
|
||||
WHERE 1 = 1
|
||||
<if test="startDate != null and startDate != ''">
|
||||
AND DATE(e.INSDATE) >= #{startDate}
|
||||
</if>
|
||||
<if test="endDate != null and endDate != ''">
|
||||
AND DATE(e.INSDATE) <= #{endDate}
|
||||
</if>
|
||||
AND e.START_DATE IS NOT NULL
|
||||
AND e.END_DATE IS NOT NULL
|
||||
AND e.END_DATE > e.START_DATE
|
||||
AND e.EXIT_WEIGHT IS NOT NULL
|
||||
AND e.EXIT_WEIGHT > 0
|
||||
AND TIMESTAMPDIFF(MINUTE, e.START_DATE, e.END_DATE) > 0
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.klp.pocket.acid.mapper.AcidOeeMapper">
|
||||
|
||||
<!-- OEE日汇总结果映射 -->
|
||||
<resultMap id="AcidOeeDailySummaryResultMap" type="com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo">
|
||||
<result column="stat_date" property="statDate" jdbcType="VARCHAR"/>
|
||||
<result column="line_id" property="lineId" jdbcType="VARCHAR"/>
|
||||
<result column="line_name" property="lineName" jdbcType="VARCHAR"/>
|
||||
<result column="planned_time_min" property="plannedTimeMin" jdbcType="BIGINT"/>
|
||||
<result column="planned_downtime_min" property="plannedDowntimeMin" jdbcType="BIGINT"/>
|
||||
<result column="loading_time_min" property="loadingTimeMin" jdbcType="BIGINT"/>
|
||||
<result column="downtime_min" property="downtimeMin" jdbcType="BIGINT"/>
|
||||
<result column="run_time_min" property="runTimeMin" jdbcType="BIGINT"/>
|
||||
<result column="total_output_ton" property="totalOutputTon" jdbcType="DECIMAL"/>
|
||||
<result column="total_output_coil" property="totalOutputCoil" jdbcType="BIGINT"/>
|
||||
<result column="good_output_ton" property="goodOutputTon" jdbcType="DECIMAL"/>
|
||||
<result column="good_output_coil" property="goodOutputCoil" jdbcType="BIGINT"/>
|
||||
<result column="defect_output_ton" property="defectOutputTon" jdbcType="DECIMAL"/>
|
||||
<result column="defect_output_coil" property="defectOutputCoil" jdbcType="BIGINT"/>
|
||||
<result column="ideal_cycle_time_min_per_ton" property="idealCycleTimeMinPerTon" jdbcType="DECIMAL"/>
|
||||
<result column="ideal_cycle_time_min_per_coil" property="idealCycleTimeMinPerCoil" jdbcType="DECIMAL"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 查询OEE日汇总 -->
|
||||
<select id="selectDailySummary" resultMap="AcidOeeDailySummaryResultMap">
|
||||
SELECT
|
||||
DATE_FORMAT(e.INSDATE, '%Y-%m-%d') AS stat_date,
|
||||
'SY' AS line_id,
|
||||
'酸轧线' AS line_name,
|
||||
-- 计划时间:暂时使用24小时(1440分钟),后续可从计划表获取
|
||||
1440 AS planned_time_min,
|
||||
-- 计划停机:暂时为0,后续可从停机事件表中筛选stop_type='计划停机'的汇总
|
||||
0 AS planned_downtime_min,
|
||||
-- 负荷时间 = 计划时间 - 计划停机
|
||||
1440 AS loading_time_min,
|
||||
-- 停机时间:在Service层通过停机事件表聚合填充
|
||||
0 AS downtime_min,
|
||||
-- 总产量(吨):出口重量总和
|
||||
COALESCE(SUM(e.EXIT_WEIGHT), 0) AS total_output_ton,
|
||||
-- 总产量(卷):记录数
|
||||
COUNT(*) AS total_output_coil,
|
||||
-- 良品/次品:在Service层通过WMS判定后填充
|
||||
0 AS good_output_ton,
|
||||
0 AS good_output_coil,
|
||||
0 AS defect_output_ton,
|
||||
0 AS defect_output_coil,
|
||||
-- 理论节拍:在Service层通过回归数据填充
|
||||
NULL AS ideal_cycle_time_min_per_ton,
|
||||
NULL AS ideal_cycle_time_min_per_coil
|
||||
FROM klptcm1_pdo_excoil e
|
||||
WHERE DATE(e.INSDATE) BETWEEN #{startDate} AND #{endDate}
|
||||
GROUP BY DATE_FORMAT(e.INSDATE, '%Y-%m-%d')
|
||||
ORDER BY stat_date ASC
|
||||
</select>
|
||||
|
||||
<!-- 查询每日的钢卷号和重量(用于良品/次品判定) -->
|
||||
<select id="selectCoilInfoByDate" resultType="com.klp.pocket.acid.mapper.AcidOeeMapper$CoilInfoByDate">
|
||||
SELECT
|
||||
DATE_FORMAT(e.INSDATE, '%Y-%m-%d') AS statDate,
|
||||
-- 当前钢卷号:使用出口卷号(excoilid)或成品卷号(encoilid)
|
||||
COALESCE(e.EXCOILID, e.ENCOILID) AS coilNo,
|
||||
-- 重量(吨):出口重量
|
||||
e.EXIT_WEIGHT AS weight
|
||||
FROM klptcm1_pdo_excoil e
|
||||
WHERE DATE(e.INSDATE) BETWEEN #{startDate} AND #{endDate}
|
||||
AND e.EXIT_WEIGHT IS NOT NULL
|
||||
AND e.EXIT_WEIGHT > 0
|
||||
AND (e.EXCOILID IS NOT NULL OR e.ENCOILID IS NOT NULL)
|
||||
ORDER BY e.INSDATE ASC, e.ENCOILID ASC
|
||||
</select>
|
||||
|
||||
<!-- 查询卷级生产节拍(min/吨):(END_DATE - START_DATE)/EXIT_WEIGHT -->
|
||||
<select id="selectCoilCycleMinPerTon" resultType="java.math.BigDecimal">
|
||||
SELECT
|
||||
-- 生产节拍(分钟/吨)
|
||||
TIMESTAMPDIFF(MINUTE, e.START_DATE, e.END_DATE) / e.EXIT_WEIGHT AS cycle_min_per_ton
|
||||
FROM klptcm1_pdo_excoil e
|
||||
WHERE 1 = 1
|
||||
<if test="startDate != null and startDate != ''">
|
||||
AND DATE(e.INSDATE) >= #{startDate}
|
||||
</if>
|
||||
<if test="endDate != null and endDate != ''">
|
||||
AND DATE(e.INSDATE) <= #{endDate}
|
||||
</if>
|
||||
AND e.START_DATE IS NOT NULL
|
||||
AND e.END_DATE IS NOT NULL
|
||||
AND e.END_DATE > e.START_DATE
|
||||
AND e.EXIT_WEIGHT IS NOT NULL
|
||||
AND e.EXIT_WEIGHT > 0
|
||||
AND TIMESTAMPDIFF(MINUTE, e.START_DATE, e.END_DATE) > 0
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.klp.pocket.acid.mapper.AcidOeeMasterMapper">
|
||||
|
||||
<!-- OEE日汇总结果映射 -->
|
||||
<resultMap id="AcidOeeDailySummaryResultMap" type="com.klp.pocket.acid.domain.vo.AcidOeeDailySummaryVo">
|
||||
<result column="stat_date" property="statDate" jdbcType="VARCHAR"/>
|
||||
<result column="line_id" property="lineId" jdbcType="VARCHAR"/>
|
||||
<result column="line_name" property="lineName" jdbcType="VARCHAR"/>
|
||||
<result column="planned_time_min" property="plannedTimeMin" jdbcType="BIGINT"/>
|
||||
<result column="planned_downtime_min" property="plannedDowntimeMin" jdbcType="BIGINT"/>
|
||||
<result column="loading_time_min" property="loadingTimeMin" jdbcType="BIGINT"/>
|
||||
<result column="downtime_min" property="downtimeMin" jdbcType="BIGINT"/>
|
||||
<result column="run_time_min" property="runTimeMin" jdbcType="BIGINT"/>
|
||||
<result column="total_output_ton" property="totalOutputTon" jdbcType="DECIMAL"/>
|
||||
<result column="total_output_coil" property="totalOutputCoil" jdbcType="BIGINT"/>
|
||||
<result column="good_output_ton" property="goodOutputTon" jdbcType="DECIMAL"/>
|
||||
<result column="good_output_coil" property="goodOutputCoil" jdbcType="BIGINT"/>
|
||||
<result column="defect_output_ton" property="defectOutputTon" jdbcType="DECIMAL"/>
|
||||
<result column="defect_output_coil" property="defectOutputCoil" jdbcType="BIGINT"/>
|
||||
<result column="ideal_cycle_time_min_per_ton" property="idealCycleTimeMinPerTon" jdbcType="DECIMAL"/>
|
||||
<result column="ideal_cycle_time_min_per_coil" property="idealCycleTimeMinPerCoil" jdbcType="DECIMAL"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 查询OEE日汇总(总产量统一使用主库 wms_material_coil) -->
|
||||
<select id="selectDailySummary" resultMap="AcidOeeDailySummaryResultMap">
|
||||
SELECT
|
||||
DATE_FORMAT(mc.create_time, '%Y-%m-%d') AS stat_date,
|
||||
'SY' AS line_id,
|
||||
'酸轧线' AS line_name,
|
||||
1440 AS planned_time_min,
|
||||
0 AS planned_downtime_min,
|
||||
1440 AS loading_time_min,
|
||||
0 AS downtime_min,
|
||||
COALESCE(SUM(mc.net_weight), 0) AS total_output_ton,
|
||||
COUNT(*) AS total_output_coil,
|
||||
0 AS good_output_ton,
|
||||
0 AS good_output_coil,
|
||||
0 AS defect_output_ton,
|
||||
0 AS defect_output_coil,
|
||||
NULL AS ideal_cycle_time_min_per_ton,
|
||||
NULL AS ideal_cycle_time_min_per_coil
|
||||
FROM wms_material_coil mc
|
||||
WHERE DATE(mc.create_time) BETWEEN #{startDate} AND #{endDate}
|
||||
AND mc.create_by = #{createBy}
|
||||
AND mc.del_flag = 0
|
||||
AND mc.net_weight IS NOT NULL
|
||||
AND mc.net_weight > 0
|
||||
GROUP BY DATE_FORMAT(mc.create_time, '%Y-%m-%d')
|
||||
|
||||
</select>
|
||||
|
||||
<!-- 查询每日的钢卷号、重量、判级(主库wms_material_coil) -->
|
||||
<select id="selectCoilInfoByDate" resultType="com.klp.pocket.acid.domain.vo.AcidOeeCoilInfoByDateVo">
|
||||
SELECT
|
||||
DATE_FORMAT(mc.create_time, '%Y-%m-%d') AS statDate,
|
||||
mc.current_coil_no AS coilNo,
|
||||
(mc.net_weight) AS weight,
|
||||
mc.quality_status AS qualityStatus
|
||||
FROM wms_material_coil mc
|
||||
WHERE DATE(mc.create_time) BETWEEN #{startDate} AND #{endDate}
|
||||
AND mc.create_by = #{createBy}
|
||||
AND mc.del_flag = 0
|
||||
AND mc.net_weight IS NOT NULL
|
||||
AND mc.net_weight > 0
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -1,5 +1,5 @@
|
||||
# 页面标题
|
||||
VUE_APP_TITLE = 科伦普冷轧涂镀数智运营一体化平台
|
||||
VUE_APP_TITLE = MES一体化平台
|
||||
|
||||
# 开发环境配置
|
||||
ENV = 'development'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 页面标题
|
||||
VUE_APP_TITLE = 科伦普冷轧涂镀数智运营一体化平台
|
||||
VUE_APP_TITLE = MES一体化平台
|
||||
|
||||
# 生产环境配置
|
||||
ENV = 'production'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 页面标题
|
||||
VUE_APP_TITLE = 科伦普冷轧涂镀数智运营一体化平台
|
||||
VUE_APP_TITLE = MES一体化平台
|
||||
|
||||
# 开发环境配置
|
||||
ENV = 'development'
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.png">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= webpackConfig.name %></title>
|
||||
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
|
||||
<style>
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
export function fetchQuickSheetList(params) {
|
||||
return request({
|
||||
url: '/aps/quick-sheet/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function fetchQuickSheetPreset(params) {
|
||||
return request({
|
||||
url: '/aps/quick-sheet/preset',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function saveQuickSheet(data) {
|
||||
return request({
|
||||
url: '/aps/quick-sheet/save',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function exportQuickSheet(params) {
|
||||
return request({
|
||||
url: '/aps/quick-sheet/export',
|
||||
method: 'get',
|
||||
params,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteQuickSheetRow(quickSheetId) {
|
||||
return request({
|
||||
url: '/aps/quick-sheet/delete',
|
||||
method: 'post',
|
||||
params: { quickSheetId }
|
||||
})
|
||||
}
|
||||
@@ -8,10 +8,3 @@ export function getAcidTypingPrefill(currentCoilNo) {
|
||||
})
|
||||
}
|
||||
|
||||
export function getGalvanize1TypingPrefill(params) {
|
||||
return request({
|
||||
url: '/pocket/galvanize1/crmPdoExcoil/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
@@ -27,16 +27,3 @@ export function delOss(ossId) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*/
|
||||
export function uploadFile(file) {
|
||||
const form = new FormData()
|
||||
form.append('file', file)
|
||||
return request({
|
||||
url: '/system/oss/upload',
|
||||
method: 'post',
|
||||
data: form,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询退火炉列表
|
||||
export function listAnnealFurnace(query) {
|
||||
return request({
|
||||
url: '/wms/anneal/furnace/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询退火炉详情
|
||||
export function getAnnealFurnace(furnaceId) {
|
||||
return request({
|
||||
url: '/wms/anneal/furnace/' + furnaceId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增退火炉
|
||||
export function addAnnealFurnace(data) {
|
||||
return request({
|
||||
url: '/wms/anneal/furnace/add',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改退火炉
|
||||
export function updateAnnealFurnace(data) {
|
||||
return request({
|
||||
url: '/wms/anneal/furnace/edit',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 启用停用
|
||||
export function changeAnnealFurnaceStatus(data) {
|
||||
return request({
|
||||
url: '/wms/anneal/furnace/status',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 置忙/置闲
|
||||
export function changeAnnealFurnaceBusy(data) {
|
||||
return request({
|
||||
url: '/wms/anneal/furnace/busy',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除退火炉
|
||||
export function delAnnealFurnace(furnaceId) {
|
||||
return request({
|
||||
url: '/wms/anneal/furnace/' + furnaceId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询退火总览信息
|
||||
export function getAnnealOverview() {
|
||||
return request({
|
||||
url: '/wms/anneal/overview',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询炉火实绩
|
||||
export function getAnnealPerformance(query) {
|
||||
return request({
|
||||
url: '/wms/anneal/performance',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询退火计划列表
|
||||
export function listAnnealPlan(query) {
|
||||
return request({
|
||||
url: '/wms/anneal/plan/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询退火计划详情
|
||||
export function getAnnealPlan(planId) {
|
||||
return request({
|
||||
url: '/wms/anneal/plan/' + planId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增退火计划
|
||||
export function addAnnealPlan(data) {
|
||||
return request({
|
||||
url: '/wms/anneal/plan/add',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改退火计划
|
||||
export function updateAnnealPlan(data) {
|
||||
return request({
|
||||
url: '/wms/anneal/plan/edit',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除退火计划
|
||||
export function delAnnealPlan(planId) {
|
||||
return request({
|
||||
url: '/wms/anneal/plan/' + planId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 更新计划状态
|
||||
export function changeAnnealPlanStatus(data) {
|
||||
return request({
|
||||
url: '/wms/anneal/plan/status',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 入炉
|
||||
export function inFurnace(data) {
|
||||
return request({
|
||||
url: '/wms/anneal/plan/in-furnace',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 完成退火
|
||||
export function completeAnnealPlan(data) {
|
||||
return request({
|
||||
url: '/wms/anneal/plan/complete',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询计划钢卷列表
|
||||
export function listAnnealPlanCoils(planId) {
|
||||
return request({
|
||||
url: '/wms/anneal/plan/coil/list',
|
||||
method: 'get',
|
||||
params: { planId }
|
||||
})
|
||||
}
|
||||
|
||||
// 绑定钢卷
|
||||
export function bindAnnealPlanCoils(data) {
|
||||
return request({
|
||||
url: '/wms/anneal/plan/coil/bind',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 解绑钢卷
|
||||
export function unbindAnnealPlanCoil(data) {
|
||||
return request({
|
||||
url: '/wms/anneal/plan/coil/unbind',
|
||||
method: 'delete',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新钢卷绑定信息
|
||||
*/
|
||||
export function updateAnnealPlanCoil(data) {
|
||||
return request({
|
||||
url: '/wms/furnacePlanCoil',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
@@ -42,27 +42,3 @@ export function delApproval(approvalId) {
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销审批
|
||||
*/
|
||||
export function withdrawApproval(approvalId) {
|
||||
return request({
|
||||
url: '/wms/approval/cancel',
|
||||
method: 'post',
|
||||
params: {
|
||||
approvalId: approvalId
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 按业务ID查询审批信息(用于用印等业务)
|
||||
*/
|
||||
export function getApprovalByBizId(bizId) {
|
||||
return request({
|
||||
url: '/wms/approval/getByBizId',
|
||||
method: 'get',
|
||||
params: { bizId }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import request from '@/utils/request'
|
||||
import { tansParams } from "@/utils/klp";
|
||||
|
||||
// 查询钢卷物料表列表
|
||||
export function listMaterialCoil(query) {
|
||||
@@ -116,8 +115,8 @@ export function splitMaterialCoil(data) {
|
||||
// 钢卷合卷
|
||||
export function mergeMaterialCoil(data) {
|
||||
return request({
|
||||
url: '/wms/materialCoil/merge',
|
||||
method: 'post',
|
||||
url: '/wms/materialCoil',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
@@ -190,8 +189,7 @@ export function listCoilWithIds(data) {
|
||||
return request({
|
||||
url: '/wms/materialCoil/listByPost',
|
||||
method: 'post',
|
||||
data,
|
||||
timeout: 600000
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -204,14 +202,13 @@ export function cancelExportCoil(coilId) {
|
||||
}
|
||||
|
||||
// 检查入场钢卷号或当前钢卷号是否合法(是否存在)
|
||||
export function checkCoilNo({ currentCoilNo, enterCoilNo, coilId, supplierCoilNo }) {
|
||||
export function checkCoilNo({ currentCoilNo, enterCoilNo, coilId }) {
|
||||
return request({
|
||||
url: '/wms/materialCoil/checkCoilNoDuplicate',
|
||||
method: 'get',
|
||||
params: {
|
||||
currentCoilNo,
|
||||
enterCoilNo,
|
||||
supplierCoilNo,
|
||||
coilId
|
||||
}
|
||||
})
|
||||
@@ -255,19 +252,15 @@ export function restoreMaterialCoil(coilId) {
|
||||
/**
|
||||
* 开始分条,锁定钢卷
|
||||
*/
|
||||
export function startSpecialSplit(coilId, actionType) {
|
||||
export function startSpecialSplit(coilId) {
|
||||
if (!coilId) {
|
||||
return Promise.reject(new Error('coilId is required'))
|
||||
}
|
||||
if (!actionType) {
|
||||
return Promise.reject(new Error('actionType is required'))
|
||||
}
|
||||
return request({
|
||||
url: '/wms/materialCoil/specialSplit/start',
|
||||
method: 'post',
|
||||
params: {
|
||||
coilId,
|
||||
actionType
|
||||
coilId
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -352,30 +345,4 @@ export function categoryWidthStatistics() {
|
||||
url: '/wms/materialCoil/statistics/categoryWidthStatistics',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出钢卷的全部字段
|
||||
*/
|
||||
export function exportCoilWithAll(data) {
|
||||
return request({
|
||||
url: '/wms/materialCoil/exportAll',
|
||||
method: 'post',
|
||||
data: data,
|
||||
transformRequest: [(params) => { return tansParams(params) }],
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询带有发货绑定信息的钢卷
|
||||
*/
|
||||
export function listWithBindInfoCoil(params) {
|
||||
return request({
|
||||
url: '/wms/materialCoil/listWithBindInfo',
|
||||
method: 'get',
|
||||
params,
|
||||
timeout: 600000
|
||||
})
|
||||
}
|
||||
@@ -42,16 +42,3 @@ export function delCoilStatisticsSummary(summaryId) {
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 检查今天是否已经创建过该类型的透视表
|
||||
// 如果已经创建过,返回该透视表的id
|
||||
// 如果没有创建过,返回null
|
||||
export function checkCoilStatisticsSummaryExist(statType) {
|
||||
return request({
|
||||
url: '/wms/coilStatisticsSummary/checkToday',
|
||||
method: 'get',
|
||||
params: {
|
||||
statType
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询员工异动(入职/离职)列表
|
||||
export function listEmployeeChange(query) {
|
||||
return request({
|
||||
url: '/wms/employeeChange/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询员工异动(入职/离职)详细
|
||||
export function getEmployeeChange(changeId) {
|
||||
return request({
|
||||
url: '/wms/employeeChange/' + changeId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增员工异动(入职/离职)
|
||||
export function addEmployeeChange(data) {
|
||||
return request({
|
||||
url: '/wms/employeeChange',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改员工异动(入职/离职)
|
||||
export function updateEmployeeChange(data) {
|
||||
return request({
|
||||
url: '/wms/employeeChange',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除员工异动(入职/离职)
|
||||
export function delEmployeeChange(changeId) {
|
||||
return request({
|
||||
url: '/wms/employeeChange/' + changeId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 员工入职
|
||||
export function employeeEntry(data) {
|
||||
return request({
|
||||
url: '/wms/employeeChange/entry',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 员工离职
|
||||
*/
|
||||
export function employeeLeave(data) {
|
||||
return request({
|
||||
url: '/wms/employeeChange/leave',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 员工转正
|
||||
*/
|
||||
export function employeeRegular(data) {
|
||||
return request({
|
||||
url: '/wms/employeeChange/regular',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询员工转岗记录列表
|
||||
export function listEmployeeTransfer(query) {
|
||||
return request({
|
||||
url: '/wms/employeeTransfer/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询员工转岗记录详细
|
||||
export function getEmployeeTransfer(transferId) {
|
||||
return request({
|
||||
url: '/wms/employeeTransfer/' + transferId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增员工转岗记录
|
||||
export function addEmployeeTransfer(data) {
|
||||
return request({
|
||||
url: '/wms/employeeTransfer',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改员工转岗记录
|
||||
export function updateEmployeeTransfer(data) {
|
||||
return request({
|
||||
url: '/wms/employeeTransfer',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除员工转岗记录
|
||||
export function delEmployeeTransfer(transferId) {
|
||||
return request({
|
||||
url: '/wms/employeeTransfer/' + transferId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 员工转岗
|
||||
export function transferEmployee(data) {
|
||||
return request({
|
||||
url: '/wms/employeeTransfer/transfer',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询打包单据列表
|
||||
export function listPacking(query) {
|
||||
return request({
|
||||
url: '/wms/coilPackingRecord/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询打包单据详细
|
||||
export function getPacking(packingId) {
|
||||
return request({
|
||||
url: '/wms/coilPackingRecord/' + packingId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增打包单据
|
||||
export function addPacking(data) {
|
||||
return request({
|
||||
url: '/wms/coilPackingRecord',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改打包单据
|
||||
export function updatePacking(data) {
|
||||
return request({
|
||||
url: '/wms/coilPackingRecord',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除打包单据
|
||||
export function delPacking(packingId) {
|
||||
return request({
|
||||
url: '/wms/coilPackingRecord/' + packingId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 查询打包明细列表
|
||||
export function listPackingDetail(query) {
|
||||
return request({
|
||||
url: '/wms/coilPackingDetail/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 新增打包明细
|
||||
export function addPackingDetail(data) {
|
||||
return request({
|
||||
url: '/wms/coilPackingDetail',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改打包明细
|
||||
export function updatePackingDetail(data) {
|
||||
return request({
|
||||
url: '/wms/coilPackingDetail',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除打包明细
|
||||
export function delPackingDetail(detailId) {
|
||||
return request({
|
||||
url: '/wms/coilPackingDetail/' + detailId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 创建打包单据及明细
|
||||
export function createPacking(data) {
|
||||
return request({
|
||||
url: '/wms/coilPackingRecord/execute',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
@@ -1,288 +0,0 @@
|
||||
/**
|
||||
* 打包模块 API 测试用例
|
||||
* 测试打包单据和明细表相关的 API 接口
|
||||
*/
|
||||
|
||||
import { expect, test, describe, beforeEach, afterEach, jest } from '@jest/globals';
|
||||
|
||||
// Mock request 模块
|
||||
jest.mock('@/utils/request', () => ({
|
||||
default: jest.fn(() => Promise.resolve({ code: 200, data: {} }))
|
||||
}));
|
||||
|
||||
import {
|
||||
listPacking,
|
||||
getPacking,
|
||||
addPacking,
|
||||
updatePacking,
|
||||
delPacking,
|
||||
listPackingDetail,
|
||||
listPackedCoil,
|
||||
addPackingDetail,
|
||||
updatePackingDetail,
|
||||
delPackingDetail,
|
||||
batchAddPackingDetail,
|
||||
getPackingByNo,
|
||||
submitPacking,
|
||||
cancelSubmitPacking
|
||||
} from './packing';
|
||||
|
||||
import request from '@/utils/request';
|
||||
|
||||
describe('Packing API Tests', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('打包单据主表 API', () => {
|
||||
test('listPacking - 查询打包单据列表', () => {
|
||||
const query = {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
packingNo: 'PK20260323001'
|
||||
};
|
||||
|
||||
listPacking(query);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packing/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
});
|
||||
|
||||
test('getPacking - 查询单个打包单据详情', () => {
|
||||
const packingId = '1';
|
||||
|
||||
getPacking(packingId);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packing/1',
|
||||
method: 'get'
|
||||
});
|
||||
});
|
||||
|
||||
test('addPacking - 新增打包单据', () => {
|
||||
const data = {
|
||||
batchNo: 'BATCH001',
|
||||
packingDate: '2026-03-23',
|
||||
team: '甲',
|
||||
operator: '张三',
|
||||
remark: '测试备注'
|
||||
};
|
||||
|
||||
addPacking(data);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packing',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
});
|
||||
|
||||
test('updatePacking - 修改打包单据', () => {
|
||||
const data = {
|
||||
packingId: '1',
|
||||
batchNo: 'BATCH001',
|
||||
packingDate: '2026-03-23',
|
||||
team: '甲',
|
||||
operator: '张三',
|
||||
remark: '修改后的备注'
|
||||
};
|
||||
|
||||
updatePacking(data);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packing',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
});
|
||||
|
||||
test('delPacking - 删除打包单据', () => {
|
||||
const packingId = '1';
|
||||
|
||||
delPacking(packingId);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packing/1',
|
||||
method: 'delete'
|
||||
});
|
||||
});
|
||||
|
||||
test('getPackingByNo - 根据单据号查询打包单据', () => {
|
||||
const packingNo = 'PK20260323001';
|
||||
|
||||
getPackingByNo(packingNo);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packing/no/PK20260323001',
|
||||
method: 'get'
|
||||
});
|
||||
});
|
||||
|
||||
test('submitPacking - 提交打包单据', () => {
|
||||
const packingId = '1';
|
||||
|
||||
submitPacking(packingId);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packing/submit/1',
|
||||
method: 'post'
|
||||
});
|
||||
});
|
||||
|
||||
test('cancelSubmitPacking - 取消提交打包单据', () => {
|
||||
const packingId = '1';
|
||||
|
||||
cancelSubmitPacking(packingId);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packing/cancelSubmit/1',
|
||||
method: 'post'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('打包明细表 API', () => {
|
||||
test('listPackingDetail - 查询打包明细列表', () => {
|
||||
const query = {
|
||||
packingId: '1',
|
||||
pageNum: 1,
|
||||
pageSize: 20
|
||||
};
|
||||
|
||||
listPackingDetail(query);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packingDetail/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
});
|
||||
|
||||
test('addPackingDetail - 新增打包明细', () => {
|
||||
const data = {
|
||||
packingId: '1',
|
||||
coilId: '1001',
|
||||
coilNo: '26L0312345',
|
||||
itemName: '镀锌板',
|
||||
specification: '1.0*1000*C',
|
||||
material: 'DX51D+Z',
|
||||
netWeight: 5.5,
|
||||
beforePosition: 'A区01号位',
|
||||
afterPosition: ''
|
||||
};
|
||||
|
||||
addPackingDetail(data);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packingDetail',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
});
|
||||
|
||||
test('updatePackingDetail - 修改打包明细', () => {
|
||||
const data = {
|
||||
detailId: '1',
|
||||
packingId: '1',
|
||||
afterPosition: 'B区02号位'
|
||||
};
|
||||
|
||||
updatePackingDetail(data);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packingDetail',
|
||||
method: 'put',
|
||||
data: data
|
||||
});
|
||||
});
|
||||
|
||||
test('delPackingDetail - 删除打包明细', () => {
|
||||
const detailId = '1';
|
||||
|
||||
delPackingDetail(detailId);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packingDetail/1',
|
||||
method: 'delete'
|
||||
});
|
||||
});
|
||||
|
||||
test('batchAddPackingDetail - 批量新增打包明细', () => {
|
||||
const data = [
|
||||
{
|
||||
packingId: '1',
|
||||
coilId: '1001',
|
||||
coilNo: '26L0312345',
|
||||
itemName: '镀锌板',
|
||||
specification: '1.0*1000*C',
|
||||
material: 'DX51D+Z',
|
||||
netWeight: 5.5,
|
||||
beforePosition: 'A区01号位',
|
||||
afterPosition: ''
|
||||
},
|
||||
{
|
||||
packingId: '1',
|
||||
coilId: '1002',
|
||||
coilNo: '26L0312346',
|
||||
itemName: '镀锌板',
|
||||
specification: '1.0*1000*C',
|
||||
material: 'DX51D+Z',
|
||||
netWeight: 5.2,
|
||||
beforePosition: 'A区02号位',
|
||||
afterPosition: ''
|
||||
}
|
||||
];
|
||||
|
||||
batchAddPackingDetail(data);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packingDetail/batch',
|
||||
method: 'post',
|
||||
data: data
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('已打包钢卷查询 API', () => {
|
||||
test('listPackedCoil - 查询已打包钢卷列表', () => {
|
||||
const query = {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
packingNo: 'PK20260323001',
|
||||
batchNo: 'BATCH001',
|
||||
coilNo: '26L0312345',
|
||||
team: '甲'
|
||||
};
|
||||
|
||||
listPackedCoil(query);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packing/packedCoilList',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
});
|
||||
|
||||
test('listPackedCoil - 带日期范围参数查询已打包钢卷', () => {
|
||||
const query = {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
params: {
|
||||
beginPackingDate: '2026-03-01',
|
||||
endPackingDate: '2026-03-31'
|
||||
}
|
||||
};
|
||||
|
||||
listPackedCoil(query);
|
||||
|
||||
expect(request).toHaveBeenCalledWith({
|
||||
url: '/wms/packing/packedCoilList',
|
||||
method: 'get',
|
||||
params: query
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,55 +1,11 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
function parseDate(date) {
|
||||
// 修复1:参数名和内部变量名冲突,改用tempDate
|
||||
// 修复2:如果传入的date为空/无效,默认使用当前时间
|
||||
const tempDate = date ? new Date(date) : new Date();
|
||||
|
||||
// 获取年、月、日、时、分、秒(补零处理,确保是两位数)
|
||||
const year = tempDate.getFullYear();
|
||||
// 月份从0开始,所以要+1,不足两位补0
|
||||
const month = String(tempDate.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(tempDate.getDate()).padStart(2, '0');
|
||||
const hours = String(tempDate.getHours()).padStart(2, '0');
|
||||
const minutes = String(tempDate.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(tempDate.getSeconds()).padStart(2, '0');
|
||||
|
||||
// 格式化为YYYY-mm-dd HH:mm:ss并返回
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
// 查询钢卷待操作列表
|
||||
export function listPendingAction(query) {
|
||||
return request({
|
||||
url: '/wms/coilPendingAction/list',
|
||||
method: 'get',
|
||||
params: query,
|
||||
timeout: 600000
|
||||
})
|
||||
}
|
||||
|
||||
// 查询钢卷待操作列表(包含已删除记录)
|
||||
// includeDeleted: 0=不包含已删除(默认), 1=包含已删除记录, 2=仅查询已删除记录
|
||||
export function listPendingActionWithDeleted(query) {
|
||||
return request({
|
||||
url: '/wms/coilPendingAction/list',
|
||||
method: 'get',
|
||||
params: {
|
||||
...query,
|
||||
includeDeleted: 1
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 仅查询已删除的钢卷待操作列表
|
||||
export function listDeletedPendingAction(query) {
|
||||
return request({
|
||||
url: '/wms/coilPendingAction/list',
|
||||
method: 'get',
|
||||
params: {
|
||||
...query,
|
||||
includeDeleted: 2
|
||||
}
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
@@ -63,37 +19,19 @@ export function getPendingAction(actionId) {
|
||||
|
||||
// 新增钢卷待操作
|
||||
export function addPendingAction(data) {
|
||||
const payload = { ...data }
|
||||
if (payload.processTime) {
|
||||
payload.processTime = parseDate(payload.processTime)
|
||||
}
|
||||
if (payload.completeTime) {
|
||||
payload.completeTime = parseDate(payload.completeTime)
|
||||
}
|
||||
return request({
|
||||
url: '/wms/coilPendingAction',
|
||||
method: 'post',
|
||||
data: payload
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改钢卷待操作
|
||||
export function updatePendingAction(data) {
|
||||
const payload = { ...data }
|
||||
if (payload.processTime) {
|
||||
payload.processTime = parseDate(payload.processTime)
|
||||
}
|
||||
if (payload.completeTime) {
|
||||
payload.completeTime = parseDate(payload.completeTime)
|
||||
}
|
||||
if (payload.scanTime) {
|
||||
// 扫码日期格式化为yyyy-MM-dd'T'HH:mm:ss.SSSX
|
||||
payload.scanTime = parseDate(payload.scanTime).replace(' ', 'T') + '.000Z'
|
||||
}
|
||||
return request({
|
||||
url: '/wms/coilPendingAction',
|
||||
method: 'put',
|
||||
data: payload
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -146,12 +84,3 @@ export function exportPendingAction(query) {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 还原被删除的钢卷
|
||||
*/
|
||||
export function restorePendingAction(actionId) {
|
||||
return request({
|
||||
url: `/wms/coilPendingAction/restore/${actionId}`,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 用印申请
|
||||
export function listSealReq(query) {
|
||||
return request({
|
||||
url: '/wms/seal/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function getSealReq(bizId) {
|
||||
return request({
|
||||
url: `/wms/seal/${bizId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function addSealReq(data) {
|
||||
return request({
|
||||
url: '/wms/seal',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function editSealReq(data) {
|
||||
return request({
|
||||
url: '/wms/seal',
|
||||
method: 'put',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function delSealReq(bizIds) {
|
||||
return request({
|
||||
url: `/wms/seal/${bizIds}`,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
export function approveSealReq(bizId, approvalOpinion) {
|
||||
return request({
|
||||
url: `/wms/seal/${bizId}/approve`,
|
||||
method: 'post',
|
||||
params: { approvalOpinion }
|
||||
})
|
||||
}
|
||||
|
||||
export function rejectSealReq(bizId, approvalOpinion) {
|
||||
return request({
|
||||
url: `/wms/seal/${bizId}/reject`,
|
||||
method: 'post',
|
||||
params: { approvalOpinion }
|
||||
})
|
||||
}
|
||||
|
||||
export function cancelSealReq(bizId) {
|
||||
return request({
|
||||
url: `/wms/seal/${bizId}/cancel`,
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
export function stampSealJava(bizId, data) {
|
||||
const payload = {
|
||||
targetFileUrl: String(data.targetFileUrl || ''),
|
||||
stampImageUrl: String(data.stampImageUrl || ''),
|
||||
pageNo: Number(data.pageNo) || 1,
|
||||
xPx: Number(data.xPx) || 0,
|
||||
yPx: Number(data.yPx) || 0,
|
||||
viewportWidth: data.viewportWidth !== undefined && data.viewportWidth !== null ? Number(data.viewportWidth) : undefined,
|
||||
viewportHeight: data.viewportHeight !== undefined && data.viewportHeight !== null ? Number(data.viewportHeight) : undefined
|
||||
}
|
||||
if (data.widthPx !== undefined && data.widthPx !== null) {
|
||||
payload.widthPx = Number(data.widthPx)
|
||||
}
|
||||
if (data.heightPx !== undefined && data.heightPx !== null) {
|
||||
payload.heightPx = Number(data.heightPx)
|
||||
}
|
||||
if (payload.viewportWidth === undefined) delete payload.viewportWidth
|
||||
if (payload.viewportHeight === undefined) delete payload.viewportHeight
|
||||
|
||||
return request({
|
||||
url: `/wms/seal/${bizId}/stamp/java`,
|
||||
method: 'post',
|
||||
data: payload
|
||||
})
|
||||
}
|
||||
|
||||
export function stampSealPython(bizId, data) {
|
||||
return request({
|
||||
url: `/wms/seal/${bizId}/stamp/python`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1773478276971" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13723" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M630.568421 619.789474h-239.831579c-10.778947 0-18.863158 8.084211-18.863158 18.863158s8.084211 18.863158 18.863158 18.863157h239.831579c10.778947 0 18.863158-8.084211 18.863158-18.863157s-8.084211-18.863158-18.863158-18.863158z" fill="#101010" p-id="13724"></path><path d="M986.273684 714.105263l-78.147368-164.378947c-5.389474-13.473684-18.863158-21.557895-32.336842-21.557895H808.421053V115.873684c0-16.168421-13.473684-29.642105-29.642106-29.642105h-99.705263c-18.863158 0-32.336842 13.473684-32.336842 32.336842v24.252632h-48.505263v-24.252632c0-18.863158-13.473684-32.336842-32.336842-32.336842h-102.4c-18.863158 0-32.336842 13.473684-32.336842 32.336842v24.252632h-48.505263v-24.252632c0-18.863158-13.473684-32.336842-32.336843-32.336842H247.915789c-16.168421 0-29.642105 13.473684-29.642105 29.642105v414.989474H150.905263c-13.473684 0-26.947368 8.084211-32.336842 21.557895l-78.147368 164.378947c-2.694737 5.389474-2.694737 10.778947-2.694737 16.168421v177.852632c0 16.168421 13.473684 29.642105 29.642105 29.642105h164.378947c10.778947 0 21.557895-5.389474 29.642106-13.473684l37.726315-45.810527h431.157895l37.726316 48.505264c8.084211 8.084211 16.168421 13.473684 29.642105 13.473684h164.378948c16.168421 0 29.642105-13.473684 29.642105-29.642106v-177.852631c-2.694737-10.778947-2.694737-16.168421-5.389474-21.557895zM512 840.757895h-158.989474l21.557895-88.926316h269.473684l24.252632 88.926316H512z m137.431579-129.347369H369.178947l-97.010526-142.821052H749.136842l-99.705263 142.821052zM256 123.957895h86.231579v24.252631c0 18.863158 13.473684 32.336842 32.336842 32.336842h59.284211c18.863158 0 32.336842-13.473684 32.336842-32.336842v-24.252631h91.621052v24.252631c0 18.863158 13.473684 32.336842 32.336842 32.336842h59.284211c18.863158 0 32.336842-13.473684 32.336842-32.336842v-24.252631h86.231579v406.905263h-512V123.957895z m8.084211 730.273684l-37.726316 45.810526H72.757895v-169.768421l78.147368-161.68421h75.452632l113.178947 167.073684-26.947368 105.094737h-18.863158c-10.778947 0-21.557895 2.694737-29.642105 13.473684z m687.157894 45.810526h-153.6l-37.726316-48.505263c-8.084211-8.084211-16.168421-13.473684-29.642105-13.473684h-24.252631l-29.642106-105.094737 118.568421-167.073684h78.147369l78.147368 164.378947v169.768421z" fill="#101010" p-id="13725"></path><path d="M563.2 476.968421c24.252632-21.557895 32.336842-53.894737 29.642105-78.147368-2.694737-13.473684-8.084211-21.557895-18.863158-24.252632-8.084211-2.694737-16.168421 0-21.557894 2.694737l-2.694737 2.694737c-2.694737 2.694737-8.084211 8.084211-10.778948 8.08421 0 0 0-2.694737 2.694737-8.08421l2.694737-2.694737c5.389474-10.778947 18.863158-48.505263-32.336842-75.452632-5.389474-2.694737-10.778947-2.694737-16.168421 0-5.389474 2.694737-8.084211 8.084211-10.778947 13.473685 0 5.389474-2.694737 13.473684-2.694737 16.168421-2.694737 2.694737-10.778947 13.473684-16.168421 21.557894-10.778947 10.778947-18.863158 21.557895-24.252632 29.642106-8.084211 10.778947-10.778947 40.421053 0 64.673684 5.389474 16.168421 21.557895 35.031579 53.894737 43.115789 8.084211 2.694737 13.473684 2.694737 18.863158 2.694737 16.168421 2.694737 35.031579-2.694737 48.505263-16.168421z m-61.978947-18.863158c-13.473684-2.694737-21.557895-10.778947-26.947369-18.863158-5.389474-13.473684-2.694737-29.642105 0-32.336842 2.694737-5.389474 13.473684-16.168421 18.863158-24.252631 10.778947-13.473684 16.168421-18.863158 18.863158-24.252632 0 2.694737 0 5.389474-2.694737 8.084211v2.694736c-5.389474 10.778947-10.778947 21.557895-5.389474 40.421053 0 2.694737 2.694737 2.694737 2.694737 5.389474 10.778947 13.473684 24.252632 18.863158 32.336842 18.863158 5.389474 0 10.778947 0 13.473685-2.694737-2.694737 8.084211-8.084211 16.168421-13.473685 21.557894-10.778947 5.389474-24.252632 8.084211-37.726315 5.389474z" fill="#101010" p-id="13726"></path><path d="M417.684211 458.105263h-37.726316v-185.936842h264.08421V458.105263h-37.726316c-10.778947 0-18.863158 8.084211-18.863157 18.863158s8.084211 18.863158 18.863157 18.863158H646.736842c18.863158 0 35.031579-16.168421 35.031579-35.031579V269.473684c0-18.863158-16.168421-35.031579-35.031579-35.031579H377.263158c-18.863158 0-35.031579 16.168421-35.031579 35.031579v194.021053c0 18.863158 16.168421 35.031579 35.031579 35.031579h40.421053c10.778947 0 18.863158-8.084211 18.863157-18.863158s-8.084211-21.557895-18.863157-21.557895z" fill="#101010" p-id="13727"></path></svg>
|
||||
|
Before Width: | Height: | Size: 4.6 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1774233338203" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5413" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M966.208 246.752L534.144 21.408a47.968 47.968 0 0 0-44.128-0.128L58.08 243.136A47.968 47.968 0 0 0 32 285.824V744.64c0 18.208 10.304 34.848 26.592 42.976l432 215.36a48 48 0 0 0 42.816 0l432-215.36A48 48 0 0 0 992 744.672V289.344c0-17.92-9.952-34.304-25.792-42.592zM508.384 463.68l-162.176-79.808 367.36-196.704 158.4 82.624-363.584 193.888z m3.488-381.696l132.992 69.376-369.312 197.76-144.896-71.328 381.216-195.808zM96 332.096l153.216 75.392v168.256a32 32 0 0 0 64 0v-136.736L480 521.024v405.184L96 734.752V332.096z m448 594.112V517.184l384-204.736v422.304l-384 191.456z" p-id="5414"></path></svg>
|
||||
|
Before Width: | Height: | Size: 932 B |
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 3.6 KiB |
@@ -18,9 +18,9 @@
|
||||
</div>
|
||||
|
||||
<el-dialog title="选择钢卷" :visible.sync="dialogVisible" :width="dialogWidth" :close-on-click-modal="false"
|
||||
@close="handleClose" append-to-body :fullscreen="orderBy">
|
||||
@close="handleClose" append-to-body>
|
||||
<!-- 搜索区域 -->
|
||||
<el-form v-if="!rangeMode" inline :model="queryParams" class="search-form">
|
||||
<el-form v-if="!rangeMode" :model="queryParams" class="search-form">
|
||||
<!-- <el-form-item label="类型">
|
||||
<el-select v-model="queryParams.selectType" placeholder="请选择类型" size="small">
|
||||
<el-option label="成品" value="product" />
|
||||
@@ -62,13 +62,12 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="实际库区" v-if="orderBy">
|
||||
<actual-warehouse-select v-model="queryParams.actualWarehouseId" placeholder="请选择实际库区" canSelectLevel2
|
||||
canSelectDisabled :clearInput="false" clearable />
|
||||
canSelectDisabled />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<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-checkbox v-if="orderBy" style="margin-left: 10px;" v-model="showCoilMap" size="small">显示钢卷地图</el-checkbox>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
@@ -78,7 +77,6 @@
|
||||
<!-- 自定义列 -->
|
||||
<el-table-column v-for="column in renderColumns" :label="column.label" :align="column.align" :prop="column.prop"
|
||||
:width="column.width" :show-overflow-tooltip="column.showOverflowTooltip" />
|
||||
<el-table-column v-if="orderBy" label="品质" prop="qualityStatus"></el-table-column>
|
||||
<el-table-column v-if="orderBy" label="切边" prop="trimmingRequirement"></el-table-column>
|
||||
<el-table-column v-if="orderBy" label="包装" prop="packagingRequirement"></el-table-column>
|
||||
</el-table>
|
||||
@@ -95,7 +93,6 @@
|
||||
<!-- 自定义列 -->
|
||||
<el-table-column v-for="column in renderColumns" :label="column.label" :align="column.align"
|
||||
:prop="column.prop" :width="column.width" :show-overflow-tooltip="column.showOverflowTooltip" />
|
||||
<el-table-column v-if="orderBy" label="品质" prop="qualityStatus"></el-table-column>
|
||||
<el-table-column v-if="orderBy" label="切边" prop="trimmingRequirement"></el-table-column>
|
||||
<el-table-column v-if="orderBy" label="包装" prop="packagingRequirement"></el-table-column>
|
||||
</el-table>
|
||||
@@ -105,23 +102,9 @@
|
||||
<pagination v-if="!rangeMode" v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
|
||||
:limit.sync="queryParams.pageSize" @pagination="getList" />
|
||||
|
||||
<div v-if="multiple && selectedCoils.length > 0" class="selected-stats">
|
||||
<div class="stats-content">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">总卷数:</span>
|
||||
<span class="stat-value">{{ totalCoils }}</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">总净重:</span>
|
||||
<span class="stat-value">{{ totalNetWeight }}t</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-table v-if="multiple && selectedCoils.length > 0" :data="selectedCoils">
|
||||
<el-table-column v-for="column in renderColumns" :label="column.label" :align="column.align" :prop="column.prop"
|
||||
:width="column.width" :show-overflow-tooltip="column.showOverflowTooltip" />
|
||||
<el-table-column v-if="orderBy" label="品质" prop="qualityStatus"></el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="50">
|
||||
<template slot-scope="scope">
|
||||
@@ -134,42 +117,23 @@
|
||||
<el-button type="primary" @click="handleConfirm">确认选择</el-button>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 一个可以拖拽和调节大小的浮层 -->
|
||||
<DragResizeBox v-if="showCoilMap" @size-change="handleSizeChange" storageKey="coil-map">
|
||||
<div style="height: 100%; width: 100%; overflow-y: scroll; display: flex; background-color: #fff;">
|
||||
<div style="min-width: 150px; position: sticky; top: 0;" v-loading="treeLoading">
|
||||
<el-tree ref="warehouseTreeRef" :data="warehouseTree" :props="treeProps" node-key="actualWarehouseId"
|
||||
@node-click="handleNodeClick" :expand-on-click-node="false" highlight-current class="warehouse-tree">
|
||||
</el-tree>
|
||||
</div>
|
||||
<warehouse-bird-mini ref="warehouseBirdMini" v-loading="warehouseLoading" :warehouseList="warehouseList"
|
||||
:id="selectedNodeId" :canToggle="false" :canRelease="false" />
|
||||
</div>
|
||||
</DragResizeBox>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listMaterialCoil } from '@/api/wms/coil';
|
||||
import { listActualWarehouse } from "@/api/wms/actualWarehouse";
|
||||
import { treeActualWarehouseTwoLevel } from "@/api/wms/actualWarehouse";
|
||||
import MemoInput from '@/components/MemoInput/index.vue';
|
||||
import MutiSelect from '@/components/MutiSelect/index.vue';
|
||||
import { defaultColumns } from './data';
|
||||
import ActualWarehouseSelect from '@/components/KLPService/ActualWarehouseSelect/index.vue';
|
||||
import WarehouseBirdMini from '@/views/wms/warehouse/components/WarehouseBirdMini.vue';
|
||||
import DragResizeBox from '@/components/DragResizeBox/index.vue';
|
||||
|
||||
export default {
|
||||
name: 'CoilSelector',
|
||||
components: {
|
||||
MemoInput,
|
||||
MutiSelect,
|
||||
ActualWarehouseSelect,
|
||||
WarehouseBirdMini,
|
||||
DragResizeBox
|
||||
ActualWarehouseSelect
|
||||
},
|
||||
dicts: ['coil_itemname', 'coil_material', 'coil_manufacturer'],
|
||||
props: {
|
||||
@@ -232,16 +196,10 @@ export default {
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否禁用O卷
|
||||
disableO: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showCoilMap: false,
|
||||
loading: false,
|
||||
coilList: [],
|
||||
total: 0,
|
||||
@@ -265,12 +223,6 @@ export default {
|
||||
columns: defaultColumns,
|
||||
currentTab: 'my',
|
||||
selectedCoils: [],
|
||||
warehouseList: [],
|
||||
selectedNodeId: null,
|
||||
warehouseLoading: false,
|
||||
warehouseTree: [],
|
||||
treeProps: { label: "actualWarehouseName", children: "children" },
|
||||
treeLoading: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -308,17 +260,6 @@ export default {
|
||||
} else {
|
||||
return this.selectedCoil ? new Set([this.selectedCoil.coilId]) : new Set();
|
||||
}
|
||||
},
|
||||
// 已选钢卷总数量
|
||||
totalCoils() {
|
||||
return this.selectedCoils.length;
|
||||
},
|
||||
// 已选钢卷总净重
|
||||
totalNetWeight() {
|
||||
return this.selectedCoils.reduce((total, item) => {
|
||||
const weight = parseFloat(item.netWeight) || 0;
|
||||
return total + weight;
|
||||
}, 0).toFixed(2);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -342,7 +283,7 @@ export default {
|
||||
// 同步到v-model
|
||||
if (this.useTrigger && val.coilId) {
|
||||
this.$emit('input', val.coilId);
|
||||
this.$emit('change', val.coilId, val);
|
||||
this.$emit('change', val.coilId);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -363,56 +304,8 @@ export default {
|
||||
if (this.initialCoil) {
|
||||
this.selectedCoil = this.initialCoil;
|
||||
}
|
||||
if (this.orderBy) {
|
||||
this.getWarehouseTree();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取库位列表
|
||||
getWarehouseList(parentId) {
|
||||
this.warehouseLoading = true;
|
||||
return listActualWarehouse({ parentId })
|
||||
.then((res) => { this.warehouseList = res.data || []; this.warehouseLoading = false; })
|
||||
.catch((err) => {
|
||||
this.$message.error("获取库位数据失败:" + err.message);
|
||||
this.warehouseList = [];
|
||||
this.warehouseLoading = false;
|
||||
});
|
||||
},
|
||||
handleNodeClick(data) {
|
||||
console.log('data', data);
|
||||
if (data.actualWarehouseType != 2) {
|
||||
return;
|
||||
}
|
||||
this.selectedNodeId = data.actualWarehouseId;
|
||||
this.getWarehouseList(data.actualWarehouseId);
|
||||
},
|
||||
// 获取树形数据
|
||||
getWarehouseTree() {
|
||||
this.treeLoading = true;
|
||||
treeActualWarehouseTwoLevel()
|
||||
.then((res) => { this.warehouseTree = res.data || []; })
|
||||
.catch((err) => { this.$message.error("获取仓库树形数据失败:" + err.message); })
|
||||
.finally(() => { this.treeLoading = false; });
|
||||
},
|
||||
// 处理大小变化
|
||||
handleSizeChange(size) {
|
||||
console.log('size', size);
|
||||
this.$refs.warehouseBirdMini.resize();
|
||||
},
|
||||
// 处理实际库区选择变化
|
||||
handleWarehouseChange(val) {
|
||||
console.log('val', val);
|
||||
if (!val) {
|
||||
this.selectedNodeId = null;
|
||||
this.warehouseList = [];
|
||||
return;
|
||||
}
|
||||
if (val.pathIds.length == 2) {
|
||||
this.selectedNodeId = val;
|
||||
this.getWarehouseList(val.id);
|
||||
}
|
||||
},
|
||||
// 动态生成表格行类名 - 综合处理选中、禁用等状态
|
||||
getRowClassName({ row }) {
|
||||
const classNames = [];
|
||||
@@ -518,10 +411,6 @@ export default {
|
||||
this.$message.warning('您没有权限选择此钢卷');
|
||||
return; // 终止后续逻辑
|
||||
}
|
||||
if (this.disableO && row.qualityStatus == 'O') {
|
||||
this.$message.warning('O卷不能选择');
|
||||
return;
|
||||
}
|
||||
this.handleSelect(row);
|
||||
},
|
||||
|
||||
@@ -553,7 +442,7 @@ export default {
|
||||
// 触发器模式下,支持v-model双向绑定
|
||||
if (this.useTrigger) {
|
||||
this.$emit('input', row.coilId);
|
||||
this.$emit('change', row.coilId, row); // 兼容v-model的change事件
|
||||
this.$emit('change', row.coilId); // 兼容v-model的change事件
|
||||
}
|
||||
// 选择后关闭对话框
|
||||
this.handleClose();
|
||||
@@ -564,7 +453,7 @@ export default {
|
||||
this.selectedCoil = null;
|
||||
this.selectedCoils = []; // 清空多选数据
|
||||
this.$emit('input', '');
|
||||
this.$emit('change', '', null); // 兼容v-model的change事件
|
||||
this.$emit('change', '');
|
||||
this.$emit('clear', true); // 触发清除事件
|
||||
},
|
||||
|
||||
@@ -650,6 +539,13 @@ export default {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
::v-deep .el-dialog__body {
|
||||
padding: 20px;
|
||||
max-height: calc(100vh - 200px);
|
||||
@@ -716,31 +612,6 @@ export default {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
// 已选钢卷统计信息样式
|
||||
.selected-stats {
|
||||
.stats-content {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
|
||||
.stat-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.stat-label {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式调整
|
||||
@media (max-width: 768px) {
|
||||
.selected-coil-info {
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
<template>
|
||||
<div class="drag-resize-container" ref="containerRef">
|
||||
<!-- 可拖拽调整的元素 -->
|
||||
<div
|
||||
class="draggable-element"
|
||||
ref="elementRef"
|
||||
:style="{
|
||||
left: `${position.x}px`,
|
||||
top: `${position.y}px`,
|
||||
width: `${size.width}px`,
|
||||
height: `${size.height}px`
|
||||
}"
|
||||
@mousedown="startDrag"
|
||||
>
|
||||
<!-- 元素内容区 -->
|
||||
<div class="element-content">
|
||||
<slot>可拖拽调整的元素</slot>
|
||||
</div>
|
||||
<!-- 右下角调整大小的控制点 -->
|
||||
<div class="resize-handle" @mousedown="startResize"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'DragResizeBox',
|
||||
props: {
|
||||
// 初始位置
|
||||
initPosition: {
|
||||
type: Object,
|
||||
default: () => ({ x: 100, y: 100 })
|
||||
},
|
||||
// 初始尺寸
|
||||
initSize: {
|
||||
type: Object,
|
||||
default: () => ({ width: 200, height: 150 })
|
||||
},
|
||||
// 移除容器尺寸限制(保留prop但默认值改为屏幕尺寸)
|
||||
containerSize: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
})
|
||||
},
|
||||
// 元素最小尺寸
|
||||
minSize: {
|
||||
type: Object,
|
||||
default: () => ({ width: 100, height: 80 })
|
||||
},
|
||||
// 用于localStorage存储的唯一标识
|
||||
storageKey: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 当前位置
|
||||
position: { x: 0, y: 0 },
|
||||
// 当前尺寸
|
||||
size: { width: 0, height: 0 },
|
||||
// 拖拽状态
|
||||
isDragging: false,
|
||||
// 调整大小状态
|
||||
isResizing: false,
|
||||
// 鼠标初始位置
|
||||
startMouse: { x: 0, y: 0 },
|
||||
// 元素初始状态
|
||||
startState: { x: 0, y: 0, width: 0, height: 0 }
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
// 初始化位置和尺寸(优先从localStorage读取)
|
||||
this.initFromStorage();
|
||||
// 监听全局鼠标移动和松开事件
|
||||
document.addEventListener('mousemove', this.handleMouseMove);
|
||||
document.addEventListener('mouseup', this.handleMouseUp);
|
||||
// 监听窗口大小变化,更新屏幕尺寸
|
||||
window.addEventListener('resize', this.updateScreenSize);
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 移除全局事件监听,防止内存泄漏
|
||||
document.removeEventListener('mousemove', this.handleMouseMove);
|
||||
document.removeEventListener('mouseup', this.handleMouseUp);
|
||||
window.removeEventListener('resize', this.updateScreenSize);
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 从localStorage初始化位置和尺寸
|
||||
* 有key时优先读取存储值,无则使用props传入的初始值
|
||||
*/
|
||||
initFromStorage() {
|
||||
if (this.storageKey) {
|
||||
try {
|
||||
const storageKey = `dnd-ps-${this.storageKey}`;
|
||||
const storedData = localStorage.getItem(storageKey);
|
||||
|
||||
if (storedData) {
|
||||
const { position, size } = JSON.parse(storedData);
|
||||
// 验证存储的数据是否合法,防止异常值
|
||||
const isValidPosition = position && typeof position.x === 'number' && typeof position.y === 'number';
|
||||
const isValidSize = size && typeof size.width === 'number' && typeof size.height === 'number';
|
||||
|
||||
if (isValidPosition && isValidSize) {
|
||||
// 使用存储的位置和尺寸(确保不小于最小尺寸)
|
||||
this.position = {
|
||||
x: Math.max(0, position.x),
|
||||
y: Math.max(0, position.y)
|
||||
};
|
||||
this.size = {
|
||||
width: Math.max(this.minSize.width, size.width),
|
||||
height: Math.max(this.minSize.height, size.height)
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('读取拖拽元素存储数据失败,使用默认值:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// 无存储数据或存储异常时,使用props初始值
|
||||
this.position = { ...this.initPosition };
|
||||
this.size = { ...this.initSize };
|
||||
},
|
||||
|
||||
/**
|
||||
* 将当前位置和尺寸保存到localStorage
|
||||
*/
|
||||
saveToStorage() {
|
||||
if (this.storageKey) {
|
||||
try {
|
||||
const storageKey = `dnd-ps-${this.storageKey}`;
|
||||
const saveData = {
|
||||
position: { ...this.position },
|
||||
size: { ...this.size },
|
||||
updateTime: new Date().getTime()
|
||||
};
|
||||
console.log('saveData', saveData);
|
||||
localStorage.setItem(storageKey, JSON.stringify(saveData));
|
||||
// 触发存储成功事件
|
||||
this.$emit('save-success', saveData);
|
||||
} catch (error) {
|
||||
console.error('保存拖拽元素数据失败:', error);
|
||||
this.$emit('save-fail', error);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新屏幕尺寸(窗口大小变化时)
|
||||
*/
|
||||
updateScreenSize() {
|
||||
this.containerSize = {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 开始拖拽(移动位置)
|
||||
*/
|
||||
startDrag(e) {
|
||||
// 阻止事件冒泡,避免和调整大小冲突
|
||||
if (e.target.classList.contains('resize-handle')) return;
|
||||
|
||||
this.isDragging = true;
|
||||
// 记录鼠标初始位置
|
||||
this.startMouse = { x: e.clientX, y: e.clientY };
|
||||
// 记录元素初始位置
|
||||
this.startState = {
|
||||
x: this.position.x,
|
||||
y: this.position.y,
|
||||
width: this.size.width,
|
||||
height: this.size.height
|
||||
};
|
||||
// 更改鼠标样式
|
||||
document.body.style.cursor = 'move';
|
||||
},
|
||||
|
||||
/**
|
||||
* 开始调整大小
|
||||
*/
|
||||
startResize(e) {
|
||||
e.stopPropagation(); // 阻止事件冒泡
|
||||
this.isResizing = true;
|
||||
// 记录鼠标初始位置
|
||||
this.startMouse = { x: e.clientX, y: e.clientY };
|
||||
// 记录元素初始尺寸
|
||||
this.startState = {
|
||||
x: this.position.x,
|
||||
y: this.position.y,
|
||||
width: this.size.width,
|
||||
height: this.size.height
|
||||
};
|
||||
// 更改鼠标样式
|
||||
document.body.style.cursor = 'se-resize';
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理鼠标移动
|
||||
*/
|
||||
handleMouseMove(e) {
|
||||
if (this.isDragging) {
|
||||
// 计算鼠标移动的偏移量
|
||||
const dx = e.clientX - this.startMouse.x;
|
||||
const dy = e.clientY - this.startMouse.y;
|
||||
|
||||
// 核心修改:移除容器边界限制,仅限制不超出屏幕左侧/顶部(右侧/底部可任意移动)
|
||||
this.position.x = Math.max(0, this.startState.x + dx);
|
||||
this.position.y = Math.max(0, this.startState.y + dy);
|
||||
|
||||
// 触发位置变化事件
|
||||
this.$emit('position-change', { ...this.position });
|
||||
}
|
||||
|
||||
if (this.isResizing) {
|
||||
// 计算鼠标移动的偏移量
|
||||
const dx = e.clientX - this.startMouse.x;
|
||||
const dy = e.clientY - this.startMouse.y;
|
||||
|
||||
// 调整大小仅限制最小尺寸,不限制屏幕边界
|
||||
this.size.width = Math.max(this.minSize.width, this.startState.width + dx);
|
||||
this.size.height = Math.max(this.minSize.height, this.startState.height + dy);
|
||||
|
||||
// 触发尺寸变化事件
|
||||
this.$emit('size-change', { ...this.size });
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 处理鼠标松开
|
||||
*/
|
||||
handleMouseUp() {
|
||||
// 重置状态
|
||||
this.isDragging = false;
|
||||
this.isResizing = false;
|
||||
// 恢复鼠标样式
|
||||
document.body.style.cursor = 'default';
|
||||
|
||||
// 保存当前状态到localStorage(有key时)
|
||||
this.saveToStorage();
|
||||
|
||||
// 触发结束事件
|
||||
this.$emit('drag-end', { position: { ...this.position }, size: { ...this.size } });
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 容器样式:改为全屏且无视觉样式 */
|
||||
.drag-resize-container {
|
||||
position: fixed; /* 固定定位覆盖整个屏幕 */
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
pointer-events: none; /* 容器不拦截鼠标事件,不影响页面其他元素 */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 可拖拽元素样式:fixed定位确保基于屏幕移动 */
|
||||
.draggable-element {
|
||||
position: fixed;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
user-select: none; /* 禁止文本选中 */
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
pointer-events: auto; /* 元素本身响应鼠标事件 */
|
||||
z-index: 99999; /* 确保元素在最上层 */
|
||||
background-color: #ffffff; /* 添加背景色,提升可视性 */
|
||||
}
|
||||
|
||||
/* 元素内容区 */
|
||||
.element-content {
|
||||
height: calc(100% - 20px);
|
||||
padding: 10px;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
/* 调整大小控制点 */
|
||||
.resize-handle {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: #1e88e5;
|
||||
cursor: se-resize;
|
||||
border-top-left-radius: 4px;
|
||||
}
|
||||
|
||||
/* 控制点hover效果 */
|
||||
.resize-handle:hover {
|
||||
background-color: #1976d2;
|
||||
}
|
||||
</style>
|
||||
@@ -1,40 +1,73 @@
|
||||
<template>
|
||||
<div class="employee-selector">
|
||||
<!-- 触发器 -->
|
||||
<div class="trigger" @click="toggleDialog">
|
||||
<div
|
||||
class="trigger"
|
||||
@click="toggleDialog"
|
||||
>
|
||||
<slot name="trigger">
|
||||
<div class="default-trigger">
|
||||
{{ displayText }}
|
||||
</div>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 选择对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="600px" append-to-body>
|
||||
<el-input v-model="searchQuery" placeholder="请输入员工姓名或部门" clearable @keyup.enter.native="handleSearch">
|
||||
<el-button slot="append" icon="el-icon-search" @click="handleSearch" />
|
||||
<el-dialog
|
||||
:title="title"
|
||||
:visible.sync="open"
|
||||
width="600px"
|
||||
append-to-body
|
||||
>
|
||||
<el-input
|
||||
v-model="searchQuery"
|
||||
placeholder="请输入员工姓名或部门"
|
||||
clearable
|
||||
@keyup.enter.native="handleSearch"
|
||||
>
|
||||
<el-button
|
||||
slot="append"
|
||||
icon="el-icon-search"
|
||||
@click="handleSearch"
|
||||
/>
|
||||
</el-input>
|
||||
|
||||
|
||||
<!-- 已选员工列表(多选模式) -->
|
||||
<div v-if="multiple && selectedEmployees.length > 0" class="selected-list">
|
||||
<div class="selected-list-title">已选员工 ({{ selectedEmployees.length }})</div>
|
||||
<el-tag v-for="employee in selectedEmployees" :key="employee[keyField]" closable
|
||||
@close="removeSelectedEmployee(employee)" class="selected-tag">
|
||||
<el-tag
|
||||
v-for="employee in selectedEmployees"
|
||||
:key="employee[keyField]"
|
||||
closable
|
||||
@close="removeSelectedEmployee(employee)"
|
||||
class="selected-tag"
|
||||
>
|
||||
{{ employee.name }} ({{ employee.dept }})
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="employeeList" style="width: 100%" height="400px" @row-click="handleRowClick"
|
||||
:row-class-name="rowClassName">
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="employeeList"
|
||||
style="width: 100%"
|
||||
height="400px"
|
||||
@row-click="handleRowClick"
|
||||
:row-class-name="rowClassName"
|
||||
>
|
||||
<el-table-column label="部门" align="center" prop="dept" />
|
||||
<el-table-column label="姓名" align="center" prop="name" />
|
||||
<el-table-column label="岗位工种" align="center" prop="jobType" />
|
||||
<el-table-column label="联系电话" align="center" prop="phone" />
|
||||
</el-table>
|
||||
|
||||
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="cancelSelection">取消</el-button>
|
||||
<el-button v-if="multiple" type="primary" @click="confirmSelection" :disabled="selectedEmployees.length === 0">
|
||||
<el-button @click="open = false">取消</el-button>
|
||||
<el-button
|
||||
v-if="multiple"
|
||||
type="primary"
|
||||
@click="confirmSelection"
|
||||
:disabled="selectedEmployees.length === 0"
|
||||
>
|
||||
确认选择 ({{ selectedEmployees.length }})
|
||||
</el-button>
|
||||
</div>
|
||||
@@ -105,9 +138,7 @@ export default {
|
||||
},
|
||||
// 处理后的员工列表,包含禁用状态
|
||||
employeeList() {
|
||||
return this.rawEmployeeList.filter(employee => {
|
||||
return employee.name?.includes(this.searchQuery) || employee.dept?.includes(this.searchQuery)
|
||||
}).map(employee => ({
|
||||
return this.rawEmployeeList.map(employee => ({
|
||||
...employee,
|
||||
disabled: this.disabledIdList.includes(employee.infoId.toString())
|
||||
}))
|
||||
@@ -142,20 +173,19 @@ export default {
|
||||
}
|
||||
this.open = !this.open
|
||||
},
|
||||
|
||||
|
||||
// 获取员工列表
|
||||
getEmployeeList() {
|
||||
this.loading = true
|
||||
const params = {
|
||||
pageNum: 1,
|
||||
pageSize: 9999,
|
||||
// name: this.searchQuery || undefined,
|
||||
// dept: this.searchQuery || undefined
|
||||
name: this.searchQuery || undefined,
|
||||
dept: this.searchQuery || undefined
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
listEmployeeInfo(params).then(response => {
|
||||
// 前端筛选员工列表,根据姓名或部门
|
||||
this.rawEmployeeList = response.rows;
|
||||
this.rawEmployeeList = response.rows
|
||||
this.loading = false
|
||||
resolve()
|
||||
}).catch(() => {
|
||||
@@ -164,19 +194,19 @@ export default {
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
// 搜索员工
|
||||
handleSearch() {
|
||||
this.getEmployeeList()
|
||||
},
|
||||
|
||||
|
||||
// 选择员工
|
||||
handleRowClick(row) {
|
||||
// 检查员工是否被禁用
|
||||
if (this.isDisabled(row)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (this.multiple) {
|
||||
// 多选模式:添加或移除员工
|
||||
const index = this.selectedEmployees.findIndex(item => item[this.keyField] === row[this.keyField])
|
||||
@@ -193,7 +223,7 @@ export default {
|
||||
this.open = false
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// 检查员工是否被禁用
|
||||
isDisabled(row) {
|
||||
const isDisabled = this.disabledIdList.includes(row[this.keyField].toString())
|
||||
@@ -202,7 +232,7 @@ export default {
|
||||
}
|
||||
return isDisabled
|
||||
},
|
||||
|
||||
|
||||
// 确认选择(多选模式)
|
||||
confirmSelection() {
|
||||
const selectedIds = this.selectedEmployees.map(item => item[this.keyField])
|
||||
@@ -210,25 +240,7 @@ export default {
|
||||
this.$emit('change', this.selectedEmployees)
|
||||
this.open = false
|
||||
},
|
||||
|
||||
cancelSelection() {
|
||||
if (this.multiple) {
|
||||
if (this.value) {
|
||||
// 多选模式:根据逗号分隔的字符串查找已选员工
|
||||
this.findSelectedEmployees(this.value.split(','))
|
||||
} else {
|
||||
this.selectedEmployees = []
|
||||
}
|
||||
} else {
|
||||
if (this.value) {
|
||||
this.findSelectedEmployee(this.value)
|
||||
} else {
|
||||
this.selectedEmployee = {}
|
||||
}
|
||||
}
|
||||
this.open = false
|
||||
},
|
||||
|
||||
|
||||
// 移除已选员工
|
||||
removeSelectedEmployee(employee) {
|
||||
const index = this.selectedEmployees.findIndex(item => item[this.keyField] === employee[this.keyField])
|
||||
@@ -236,7 +248,7 @@ export default {
|
||||
this.selectedEmployees.splice(index, 1)
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// 表格行样式
|
||||
rowClassName({ row }) {
|
||||
// if (this.isDisabled(row)) {
|
||||
@@ -247,7 +259,7 @@ export default {
|
||||
}
|
||||
return ''
|
||||
},
|
||||
|
||||
|
||||
// 检查员工是否已选
|
||||
isSelected(row) {
|
||||
if (this.multiple) {
|
||||
@@ -256,7 +268,7 @@ export default {
|
||||
return this.selectedEmployee[this.keyField] === row[this.keyField]
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// 根据value查找选中的员工(单选)
|
||||
findSelectedEmployee(value) {
|
||||
if (this.employeeList.length > 0) {
|
||||
@@ -274,18 +286,15 @@ export default {
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
// 根据value查找选中的员工(多选)
|
||||
findSelectedEmployees(values) {
|
||||
if (this.employeeList.length > 0) {
|
||||
this.selectedEmployees = this.employeeList.filter(item => values.includes(item[this.keyField]))
|
||||
// 因为员工可能出现重名,所以需要去重,保证selectedEmployees[i][this.keyField]是唯一的
|
||||
this.selectedEmployees = this.selectedEmployees.filter((item, index, arr) => arr.findIndex(t => t[this.keyField] === item[this.keyField]) === index)
|
||||
} else {
|
||||
// 如果员工列表为空,先获取列表再查找
|
||||
this.getEmployeeList().then(() => {
|
||||
this.selectedEmployees = this.employeeList.filter(item => values.includes(item[this.keyField]))
|
||||
this.selectedEmployees = this.selectedEmployees.filter((item, index, arr) => arr.findIndex(t => t[this.keyField] === item[this.keyField]) === index)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
<template>
|
||||
<div class="file-list-container">
|
||||
<el-table
|
||||
:data="fileList"
|
||||
border
|
||||
size="small"
|
||||
v-loading="loading"
|
||||
style="width: 100%;"
|
||||
>
|
||||
<el-table-column
|
||||
label="文件名"
|
||||
prop="originalName"
|
||||
min-width="200"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<i class="el-icon-document" style="margin-right: 8px;"></i>
|
||||
{{ scope.row.originalName }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="操作"
|
||||
width="100"
|
||||
align="center"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-button
|
||||
type="text"
|
||||
icon="el-icon-download"
|
||||
@click="downloadFile(scope.row)"
|
||||
size="small"
|
||||
>
|
||||
下载
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 空数据提示 -->
|
||||
<div v-if="fileList.length === 0 && !loading" class="empty-tip">
|
||||
<el-empty description="暂无文件数据"></el-empty>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listByIds } from "@/api/system/oss";
|
||||
|
||||
export default {
|
||||
name: "FileList",
|
||||
props: {
|
||||
ossIds: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fileList: [],
|
||||
loading: false // 加载状态
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
ossIds: {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
this.getFileList();
|
||||
} else {
|
||||
this.fileList = []; // 清空文件列表
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getFileList() {
|
||||
if (!this.ossIds) return;
|
||||
|
||||
this.loading = true;
|
||||
try {
|
||||
let res = await listByIds(this.ossIds);
|
||||
this.fileList = res.data || [];
|
||||
} catch (error) {
|
||||
this.$message.error('获取文件列表失败:' + (error.message || '未知错误'));
|
||||
this.fileList = [];
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 文件下载方法
|
||||
downloadFile(file) {
|
||||
if (!file || !file.ossId) {
|
||||
this.$message.warning('文件下载地址不存在');
|
||||
return;
|
||||
}
|
||||
this.$download.oss(file.ossId);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.file-list-container {
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
::v-deep .el-table {
|
||||
--el-table-header-text-color: #606266;
|
||||
--el-table-row-hover-bg-color: #f5f7fa;
|
||||
}
|
||||
</style>
|
||||
@@ -66,7 +66,7 @@ export default {
|
||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileType: {
|
||||
type: Array,
|
||||
default: () => ["doc", "docx", "xls", "xlsx", "ppt", "txt", "pdf", 'png', 'jpg', 'jpeg', 'bmp', 'webp'],
|
||||
default: () => ["doc", "xls", "xlsx", "ppt", "txt", "pdf", 'png', 'jpg', 'jpeg', 'bmp', 'webp'],
|
||||
},
|
||||
// 是否显示提示
|
||||
isShowTip: {
|
||||
|
||||
95
klp-ui/src/components/KLPService/Renderer/BomInfo.vue
Normal file
95
klp-ui/src/components/KLPService/Renderer/BomInfo.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-descriptions :column="1" border v-if="bomInfo.length > 0">
|
||||
<el-descriptions-item v-for="item in bomInfo" :key="item.attrKey" :label="item.attrKey">
|
||||
{{ item.attrValue }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div v-else>
|
||||
<el-empty description="暂无参数信息" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 如果传递了bomId直接使用bomId, 如果没有传递,则根据itemType和itemId获取bomId
|
||||
import { listBomItem } from '@/api/wms/bomItem';
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'BomInfo',
|
||||
props: {
|
||||
bomId: {
|
||||
type: [String, Number],
|
||||
required: false
|
||||
},
|
||||
itemType: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
itemId: {
|
||||
type: [String, Number],
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
bomInfo: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('category', ['bomMap', 'productMap', 'rawMaterialMap']),
|
||||
},
|
||||
watch: {
|
||||
bomId: {
|
||||
handler: function (newVal) {
|
||||
this.getBomInfo();
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
itemId: {
|
||||
handler: function (newVal) {
|
||||
this.getBomInfo();
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getBomInfo() {
|
||||
const bomMap = this.$store.getters.bomMap;
|
||||
if (!this.bomId && !this.itemType && !this.itemId) {
|
||||
return;
|
||||
}
|
||||
let bomId = this.bomId;
|
||||
if (!bomId) {
|
||||
if (this.itemType === 'product') {
|
||||
bomId = this.productMap[this.itemId]?.bomId;
|
||||
} else if (this.itemType === 'raw_material') {
|
||||
bomId = this.rawMaterialMap[this.itemId]?.bomId;
|
||||
}
|
||||
}
|
||||
if (!bomId) {
|
||||
return;
|
||||
}
|
||||
if (bomMap[bomId]) {
|
||||
this.bomInfo = bomMap[bomId];
|
||||
} else {
|
||||
listBomItem({ bomId }).then(res => {
|
||||
this.bomInfo = res.rows;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bom-info {
|
||||
cursor: pointer;
|
||||
/* 溢出隐藏显示省略号 */
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 100px;
|
||||
}
|
||||
</style>
|
||||
96
klp-ui/src/components/KLPService/Renderer/BomInfoMini.vue
Normal file
96
klp-ui/src/components/KLPService/Renderer/BomInfoMini.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="bomInfo.length > 0">
|
||||
<el-tooltip :content="bomInfo.map(item => item.attrKey + ':' + item.attrValue).join(',')" class="bom-info" placement="top">
|
||||
<span>{{ bomInfo.map(item => item.attrKey).join(',') }}</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div v-else>
|
||||
-
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 如果传递了bomId直接使用bomId, 如果没有传递,则根据itemType和itemId获取bomId
|
||||
import { listBomItem } from '@/api/wms/bomItem';
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'BomInfo',
|
||||
props: {
|
||||
bomId: {
|
||||
type: [String, Number],
|
||||
required: false
|
||||
},
|
||||
itemType: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
itemId: {
|
||||
type: [String, Number],
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
bomInfo: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('category', ['bomMap', 'productMap', 'rawMaterialMap']),
|
||||
},
|
||||
watch: {
|
||||
bomId: {
|
||||
handler: function (newVal) {
|
||||
this.getBomInfo();
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
itemId: {
|
||||
handler: function (newVal) {
|
||||
this.getBomInfo();
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getBomInfo() {
|
||||
const bomMap = this.$store.getters.bomMap;
|
||||
if (!this.bomId && !this.itemType && !this.itemId) {
|
||||
return;
|
||||
}
|
||||
let bomId = this.bomId;
|
||||
if (!bomId) {
|
||||
if (this.itemType === 'product') {
|
||||
bomId = this.productMap[this.itemId]?.bomId;
|
||||
} else if (this.itemType === 'raw_material') {
|
||||
bomId = this.rawMaterialMap[this.itemId]?.bomId;
|
||||
}
|
||||
}
|
||||
if (!bomId) {
|
||||
this.bomInfo = [];
|
||||
return;
|
||||
}
|
||||
if (bomMap[bomId]) {
|
||||
this.bomInfo = bomMap[bomId];
|
||||
} else {
|
||||
listBomItem({ bomId }).then(res => {
|
||||
this.bomInfo = res.rows;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.bom-info {
|
||||
cursor: pointer;
|
||||
/* 溢出隐藏显示省略号 */
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 100px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<span v-if="category">
|
||||
<slot :category="category">
|
||||
{{ category.categoryName }}
|
||||
</slot>
|
||||
</span>
|
||||
<span v-else>--</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'CategoryRenderer',
|
||||
props: {
|
||||
categoryId: {
|
||||
type: [String, Number],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('category', ['categoryList']),
|
||||
category() {
|
||||
return this.categoryList.find(cat => String(cat.categoryId) === String(this.categoryId));
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -30,18 +30,6 @@
|
||||
<span class="label">净重:</span>
|
||||
<span class="value">{{ netWeight }}</span>
|
||||
</div>
|
||||
<div class="info-item" v-if="length">
|
||||
<span class="label">长度:</span>
|
||||
<span class="value">{{ length }}</span>
|
||||
</div>
|
||||
<div class="info-item" v-if="actualLength">
|
||||
<span class="label">实测长度:</span>
|
||||
<span class="value">{{ actualLength }}</span>
|
||||
</div>
|
||||
<div class="info-item" v-if="actualWidth">
|
||||
<span class="label">实测宽度:</span>
|
||||
<span class="value">{{ actualWidth }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="label">厂家卷号:</span>
|
||||
<span class="value">{{ supplierCoilNo }}</span>
|
||||
@@ -80,16 +68,16 @@ export default {
|
||||
return this.coilNo && this.coilNo.startsWith('G');
|
||||
},
|
||||
specification() {
|
||||
return this.coilInfo.specification || '-'
|
||||
return this.coilInfo.product?.specification || this.coilInfo.rawMaterial?.specification || '-'
|
||||
},
|
||||
itemName() {
|
||||
return this.coilInfo.itemName || '-'
|
||||
return this.coilInfo.product?.productName || this.coilInfo.rawMaterial?.rawMaterialName || '-'
|
||||
},
|
||||
material() {
|
||||
return this.coilInfo.material || '-'
|
||||
return this.coilInfo.product?.material || this.coilInfo.rawMaterial?.material || '-'
|
||||
},
|
||||
manufacturer() {
|
||||
return this.coilInfo.manufacturer || '-'
|
||||
return this.coilInfo.product?.manufacturer || this.coilInfo.rawMaterial?.manufacturer || '-'
|
||||
},
|
||||
currentCoilNo() {
|
||||
return this.coilNo || this.coilInfo?.currentCoilNo || '-'
|
||||
@@ -99,16 +87,7 @@ export default {
|
||||
},
|
||||
supplierCoilNo() {
|
||||
return this.coilInfo.supplierCoilNo || '-'
|
||||
},
|
||||
length() {
|
||||
return this.coilInfo.length || '-'
|
||||
},
|
||||
actualLength() {
|
||||
return this.coilInfo.actualLength || '-'
|
||||
},
|
||||
actualWidth() {
|
||||
return this.coilInfo.actualWidth || '-'
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getCoilInfo() {
|
||||
|
||||
26
klp-ui/src/components/KLPService/Renderer/CraftInfo.vue
Normal file
26
klp-ui/src/components/KLPService/Renderer/CraftInfo.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<el-tag>{{ getCraftInfo() }}</el-tag>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'CraftInfo',
|
||||
props: {
|
||||
craftId: {
|
||||
type: [String, Number, undefined],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['processList'])
|
||||
},
|
||||
methods: {
|
||||
getCraftInfo() {
|
||||
const craft = this.processList.find(item => item.processId === this.craftId);
|
||||
return craft.processName;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -64,9 +64,9 @@ export default {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
productId: this.product.itemId || '',
|
||||
productName: this.product.itemName || '',
|
||||
// productCode: this.product.productCode || '',
|
||||
productId: this.product.productId || '',
|
||||
productName: this.product.productName || '',
|
||||
productCode: this.product.productCode || '',
|
||||
specification: this.product.specification || '',
|
||||
material: this.product.material || '',
|
||||
surfaceTreatment: this.product.surfaceTreatmentDesc || '',
|
||||
|
||||
119
klp-ui/src/components/KLPService/Renderer/ProductInfoCache.vue
Normal file
119
klp-ui/src/components/KLPService/Renderer/ProductInfoCache.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<template>
|
||||
<div v-loading="loading" loading-text="加载中...">
|
||||
<span class="product-name" @click.stop="clickHandle">
|
||||
<slot name="default" :product="product">
|
||||
{{ product && product.productName ? product.productName : '--' }}
|
||||
</slot>
|
||||
</span>
|
||||
<el-dialog
|
||||
:visible="showDetail"
|
||||
@close="showDetail = false"
|
||||
:title="product && product.productName ? product.productName : '--' "
|
||||
width="500px"
|
||||
append-to-body
|
||||
>
|
||||
<el-descriptions :column="1" border>
|
||||
<el-descriptions-item label="产品名称">
|
||||
{{ product.productName || '--' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="产品编码">
|
||||
{{ product.productCode || '--' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="规格">
|
||||
{{ product.specification || '--' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="材质">
|
||||
{{ product.material || '--' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="表面处理">
|
||||
{{ product.surfaceTreatment || '--' }}
|
||||
</el-descriptions-item>
|
||||
<!-- 锌层 -->
|
||||
<el-descriptions-item label="镀层质量">
|
||||
{{ product.zincLayer || '--' }}
|
||||
</el-descriptions-item>
|
||||
<!-- 厂家 -->
|
||||
<el-descriptions-item label="厂家">
|
||||
{{ product.manufacturer || '--' }}
|
||||
</el-descriptions-item>
|
||||
|
||||
</el-descriptions>
|
||||
<!-- <BomInfo :bomId="product.bomId" /> -->
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import BomInfo from './BomInfo.vue';
|
||||
|
||||
export default {
|
||||
name: 'ProductInfo',
|
||||
components: {
|
||||
BomInfo
|
||||
},
|
||||
props: {
|
||||
productId: {
|
||||
type: [String, Number],
|
||||
required: true
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
console.log(this.productId, this.productMap);
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showDetail: false,
|
||||
// product: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
productMap: state => state.category.productMap
|
||||
}),
|
||||
product() {
|
||||
// 检查 productMap 是否已加载
|
||||
if (!this.productMap || Object.keys(this.productMap).length === 0) {
|
||||
return {};
|
||||
}
|
||||
if (!this.productId) {
|
||||
return {};
|
||||
}
|
||||
return this.productMap[this.productId.toString()] || {};
|
||||
},
|
||||
loading() {
|
||||
return !this.productMap || Object.keys(this.productMap).length === 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clickHandle() {
|
||||
this.showDetail = true;
|
||||
}
|
||||
},
|
||||
// watch: {
|
||||
// productId: {
|
||||
// handler(newVal) {
|
||||
// if (!newVal) {
|
||||
// this.product = {};
|
||||
// }
|
||||
// const res = this.productMap[newVal.toString()] ? this.productMap[newVal.toString()] : {};
|
||||
// this.product = res;
|
||||
// },
|
||||
// immediate: true
|
||||
// }
|
||||
// }
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.product-name {
|
||||
color: #1890ff;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 可选:调整描述列表的外边距 */
|
||||
:deep(.el-descriptions) {
|
||||
margin-top: -10px;
|
||||
}
|
||||
</style>
|
||||
@@ -31,8 +31,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BomInfo from './BomInfo.vue'; // 保留导入(如需使用可解除注释)
|
||||
|
||||
export default {
|
||||
name: 'RawMaterialInfo',
|
||||
components: {
|
||||
BomInfo
|
||||
},
|
||||
props: {
|
||||
material: {
|
||||
type: Object,
|
||||
@@ -51,9 +56,9 @@ export default {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
rawMaterialId: this.material.itemId || '',
|
||||
rawMaterialName: this.material.itemName || '',
|
||||
// rawMaterialCode: this.material.rawMaterialCode || '',
|
||||
rawMaterialId: this.material.rawMaterialId || '',
|
||||
rawMaterialName: this.material.rawMaterialName || '',
|
||||
rawMaterialCode: this.material.rawMaterialCode || '',
|
||||
specification: this.material.specification || '',
|
||||
material: this.material.material || '',
|
||||
surfaceTreatment: this.material.surfaceTreatmentDesc || '',
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div v-loading="loading" loading-text="加载中...">
|
||||
<!-- 作用域插槽 -->
|
||||
<span class="material-name" @click.stop="showDetail = true">
|
||||
<slot name="default" :material="material">
|
||||
{{ material.rawMaterialName ? material.rawMaterialName : '-' }}
|
||||
</slot>
|
||||
</span>
|
||||
<el-dialog :visible="showDetail" @close="showDetail = false" :title="material.rawMaterialName" width="600px"
|
||||
append-to-body>
|
||||
<el-descriptions :column="1" border>
|
||||
<!-- <el-descriptions-item label="原材料ID">{{ material.rawMaterialId }}</el-descriptions-item> -->
|
||||
<el-descriptions-item label="原材料名称">{{ material.rawMaterialName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="原材料编码">{{ material.rawMaterialCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="规格">{{ material.specification }}</el-descriptions-item>
|
||||
<el-descriptions-item label="材质">
|
||||
{{ material.material || '--' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="表面处理">
|
||||
{{ material.surfaceTreatment || '--' }}
|
||||
</el-descriptions-item>
|
||||
<!-- 锌层 -->
|
||||
<el-descriptions-item label="镀层质量">
|
||||
{{ material.zincLayer || '--' }}
|
||||
</el-descriptions-item>
|
||||
<!-- 厂家 -->
|
||||
<el-descriptions-item label="厂家">
|
||||
{{ material.manufacturer || '--' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<!-- <BomInfo :bomId="material.bomId" /> -->
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import BomInfo from './BomInfo.vue';
|
||||
|
||||
export default {
|
||||
name: 'RawMaterialInfo',
|
||||
components: {
|
||||
BomInfo
|
||||
},
|
||||
props: {
|
||||
materialId: {
|
||||
type: [String, Number],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showDetail: false,
|
||||
// material: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
materialMap: state => state.category.rawMaterialMap // 假设vuex中为material模块
|
||||
}),
|
||||
material() {
|
||||
// 检查 materialMap 是否已加载
|
||||
if (!this.materialMap || Object.keys(this.materialMap).length === 0) {
|
||||
return {};
|
||||
}
|
||||
if (!this.materialId) {
|
||||
return {};
|
||||
}
|
||||
return this.materialMap[this.materialId.toString()] || {};
|
||||
},
|
||||
loading() {
|
||||
return !this.materialMap || Object.keys(this.materialMap).length === 0
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.material-name {
|
||||
color: #1890ff;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
@@ -1,8 +1,10 @@
|
||||
export { default as CheckItemSelect } from './CheckItemSelect/index.vue';
|
||||
export { default as ProductSelect } from './ProductSelect/index.vue';
|
||||
export { default as RawMaterialSelect } from './RawMaterialSelect/index.vue';
|
||||
export { default as CategoryRenderer } from './Renderer/CategoryRenderer.vue';
|
||||
export { default as UserSelect } from './UserSelect/index.vue';
|
||||
export { default as WarehouseSelect } from './WarehouseSelect/index.vue';
|
||||
export { default as ProductInfo } from './Renderer/ProductInfo.vue';
|
||||
export { default as RawMaterialInfo } from './Renderer/RawMaterialInfo.vue';
|
||||
export { default as BomInfoMini } from './Renderer/BomInfoMini.vue';
|
||||
export { default as WarehouseTree } from './WarehouseTree/index.vue';
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="el-table-container" ref="elTableWrapper" @mouseleave="handleTableLeave">
|
||||
<!-- 原生 Table 核心:透传 props/事件/插槽 -->
|
||||
<el-table :ref="tableRef" v-bind="$attrs" v-on="$listeners" :class="['my-table', customClass]"
|
||||
@cell-mouse-enter="handleCellEnter" @row-mouseleave="handleRowLeave" :height="height">
|
||||
@cell-mouse-enter="handleCellEnter" @row-mouseleave="handleRowLeave">
|
||||
<!-- 2. 透传原生内置插槽(如 empty 空数据插槽、append 底部插槽等) -->
|
||||
<template v-slot:empty="scope">
|
||||
<slot name="empty" v-bind="scope"></slot>
|
||||
@@ -80,10 +80,6 @@ export default {
|
||||
columns: [],
|
||||
title: '详细信息'
|
||||
})
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
<template>
|
||||
<div class="time-input-group">
|
||||
<el-date-picker v-model="dateValue" type="date" value-format="yyyy-MM-dd" placeholder="选择日期" style="width: 140px;" @change="updateDateTime" />
|
||||
<span class="time-separator">@</span>
|
||||
<el-input-number :controls="false" v-model="hourValue" placeholder="时" min="0" max="23" style="width: 60px;" @change="updateDateTime" />
|
||||
<span class="time-separator">:</span>
|
||||
<el-input-number :controls="false" v-model="minuteValue" placeholder="分" min="0" max="59" style="width: 60px;" @change="updateDateTime" />
|
||||
<span class="time-separator">:00</span>
|
||||
<el-button v-if="showNowButton" type="text" size="small" @click="setToNow" style="margin-left: 8px;">此刻</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TimeInput',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
showNowButton: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dateValue: '',
|
||||
hourValue: '',
|
||||
minuteValue: ''
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(newValue) {
|
||||
this.parseDateTime(newValue);
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
parseDateTime(dateTimeStr) {
|
||||
if (!dateTimeStr) {
|
||||
this.dateValue = '';
|
||||
this.hourValue = '';
|
||||
this.minuteValue = '';
|
||||
return;
|
||||
}
|
||||
const date = new Date(dateTimeStr);
|
||||
if (!isNaN(date.getTime())) {
|
||||
this.dateValue = date.toISOString().split('T')[0];
|
||||
this.hourValue = date.getHours();
|
||||
this.minuteValue = date.getMinutes();
|
||||
}
|
||||
},
|
||||
updateDateTime() {
|
||||
if (this.dateValue && this.hourValue !== '' && this.minuteValue !== '') {
|
||||
const dateTimeStr = `${this.dateValue} ${this.hourValue}:${this.minuteValue}:00`;
|
||||
this.$emit('input', dateTimeStr);
|
||||
} else {
|
||||
this.$emit('input', '');
|
||||
}
|
||||
},
|
||||
setToNow() {
|
||||
const now = new Date();
|
||||
this.dateValue = now.toISOString().split('T')[0];
|
||||
this.hourValue = now.getHours();
|
||||
this.minuteValue = now.getMinutes();
|
||||
this.updateDateTime();
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.time-input-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.time-separator {
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
@@ -35,7 +35,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
title: '科伦普一体化平台',
|
||||
title: 'MES一体化平台',
|
||||
logo: logoImg
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,10 +48,6 @@ import KLPTable from '@/components/KLPUI/KLPTable/index.vue'
|
||||
import MemoInput from '@/components/MemoInput/index.vue'
|
||||
import CurrentCoilNo from '@/components/KLPService/Renderer/CurrentCoilNo.vue'
|
||||
|
||||
// 初始化所有列
|
||||
import { initAllColumns } from '@/views/wms/report/js/column.js'
|
||||
|
||||
|
||||
|
||||
// 全局方法挂载
|
||||
Vue.prototype.getDicts = getDicts
|
||||
@@ -87,9 +83,6 @@ Vue.use(VueKonva);
|
||||
Vue.use(dashboardBigPlugin)
|
||||
DictData.install()
|
||||
|
||||
// 初始化所有列
|
||||
initAllColumns()
|
||||
|
||||
/**
|
||||
* If you don't want to use mock-server
|
||||
* you want to use MockJs for mock api
|
||||
|
||||
@@ -125,19 +125,6 @@ export const constantRoutes = [
|
||||
meta: { title: '工厂总日历' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/wms/seal',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: 'sealDetail/:bizId',
|
||||
component: () => import('@/views/wms/seal/sealDetail'),
|
||||
name: 'WmsSealDetail',
|
||||
meta: { title: '用印详情' }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -1,699 +0,0 @@
|
||||
<template>
|
||||
<div class="aps-quick-sheet excel-theme">
|
||||
<div class="sheet-toolbar">
|
||||
<div class="sheet-actions">
|
||||
<el-button size="small" icon="el-icon-refresh" @click="loadRows">刷新</el-button>
|
||||
<el-button size="small" type="primary" icon="el-icon-finished" :loading="saving" @click="saveAll">保存</el-button>
|
||||
<el-button size="small" icon="el-icon-plus" @click="addRow">新增行</el-button>
|
||||
<el-button size="small" icon="el-icon-download" @click="exportCsv">导出</el-button>
|
||||
<el-button size="small" icon="el-icon-setting" @click="columnSettingVisible = true">列设置</el-button>
|
||||
<el-date-picker
|
||||
v-model="filter.range"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="yyyy-MM-dd"
|
||||
size="small"
|
||||
style="width: 240px"
|
||||
@change="onFilterChange"
|
||||
/>
|
||||
</div>
|
||||
<div class="preset-bar">
|
||||
<span class="preset-title">预设方案:</span>
|
||||
<el-button
|
||||
v-for="line in lineOptions"
|
||||
:key="line.lineId"
|
||||
size="mini"
|
||||
plain
|
||||
@click="applyPreset(line)"
|
||||
>
|
||||
{{ line.lineName || line.lineCode || ('产线' + line.lineId) }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sheet-body">
|
||||
<el-table
|
||||
ref="quickSheetTable"
|
||||
v-loading="loading"
|
||||
:data="displayRows"
|
||||
border
|
||||
size="mini"
|
||||
class="excel-table"
|
||||
>
|
||||
<el-table-column label="序号" width="60" align="center" fixed="left">
|
||||
<template slot-scope="scope">
|
||||
{{ ((pager.pageNum - 1) * pager.pageSize) + scope.$index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-for="(col, colIndex) in displayColumns"
|
||||
:key="col.prop || col.label"
|
||||
:label="col.label"
|
||||
:prop="col.prop"
|
||||
:width="col.width"
|
||||
:min-width="col.minWidth"
|
||||
:align="col.align || 'center'"
|
||||
:fixed="fixedProps.includes(col.prop) ? 'left' : false"
|
||||
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<template v-if="isTimeField(col.prop)">
|
||||
<el-date-picker
|
||||
v-model="scope.row[col.prop]"
|
||||
type="datetime"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
size="mini"
|
||||
style="width: 100%"
|
||||
@change="onCellChange(scope.row)"
|
||||
@input="onCellChange(scope.row)"
|
||||
@keydown.native.enter.prevent="focusNextCell(scope.$index, colIndex, 'down')"
|
||||
@keydown.native.tab.prevent="focusNextCell(scope.$index, colIndex, 'right')"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-autocomplete
|
||||
v-if="col.prop === 'lineName'"
|
||||
v-model="scope.row[col.prop]"
|
||||
size="mini"
|
||||
clearable
|
||||
:fetch-suggestions="queryLineHistory"
|
||||
@select="onLineHistorySelect(scope.row)"
|
||||
@input="onLineHistoryInput(scope.row)"
|
||||
@keydown.native.enter.prevent="focusNextCell(scope.$index, colIndex, 'down')"
|
||||
@keydown.native.tab.prevent="focusNextCell(scope.$index, colIndex, 'right')"
|
||||
/>
|
||||
<el-input
|
||||
v-else
|
||||
v-model="scope.row[col.prop]"
|
||||
size="mini"
|
||||
clearable
|
||||
@input="onCellChange(scope.row)"
|
||||
@keydown.native.enter.prevent="focusNextCell(scope.$index, colIndex, 'down')"
|
||||
@keydown.native.tab.prevent="focusNextCell(scope.$index, colIndex, 'right')"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="70" fixed="right" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" @click="clearRow(scope.row)">清空</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="pager-wrap">
|
||||
<el-pagination
|
||||
small
|
||||
background
|
||||
layout="prev, pager, next, total"
|
||||
:current-page.sync="pager.pageNum"
|
||||
:page-size="pager.pageSize"
|
||||
:total="totalRows"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="missingRows.length" class="missing-tip">
|
||||
<div v-for="msg in missingRows" :key="msg" class="missing-item">{{ msg }}</div>
|
||||
</div>
|
||||
<el-drawer
|
||||
title="列设置"
|
||||
:visible.sync="columnSettingVisible"
|
||||
direction="rtl"
|
||||
size="55%"
|
||||
append-to-body
|
||||
@open="resetColumnDraft">
|
||||
<div class="col-setting-body">
|
||||
<div class="col-setting-toolbar">
|
||||
<el-button size="mini" @click="restoreDefaultColumns">恢复默认</el-button>
|
||||
<el-button size="mini" @click="resetColumnDraft">重置</el-button>
|
||||
<el-button size="mini" type="primary" @click="saveColumnSettings">保存</el-button>
|
||||
</div>
|
||||
|
||||
<div class="col-section">
|
||||
<div class="col-section-title">固定列(最多5列)</div>
|
||||
<draggable v-model="fixedColumnList" handle=".drag-handle" animation="200" class="col-setting-grid">
|
||||
<div v-for="item in fixedColumnList" :key="`fixed-${item.prop}`" class="col-setting-item">
|
||||
<i class="el-icon-rank drag-handle" />
|
||||
<span class="col-label">{{ item.label }}</span>
|
||||
<el-button type="text" size="mini" @click="removeFromFixed(item.prop)">移除</el-button>
|
||||
</div>
|
||||
</draggable>
|
||||
</div>
|
||||
|
||||
<div class="col-section">
|
||||
<div class="col-section-title">其他列</div>
|
||||
<draggable v-model="normalColumnList" handle=".drag-handle" animation="200" class="col-setting-grid">
|
||||
<div v-for="item in normalColumnList" :key="`normal-${item.prop}`" class="col-setting-item">
|
||||
<i class="el-icon-rank drag-handle" />
|
||||
<span class="col-label">{{ item.label }}</span>
|
||||
<el-button type="text" size="mini" @click="addToFixed(item.prop)">加入固定</el-button>
|
||||
</div>
|
||||
</draggable>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fetchQuickSheetList, fetchQuickSheetPreset, saveQuickSheet, exportQuickSheet, deleteQuickSheetRow } from '@/api/aps/quickSheet'
|
||||
import { listProductionLine } from '@/api/wms/productionLine'
|
||||
import { getTemplateByKey } from './sheets/templates'
|
||||
import { saveAs } from 'file-saver'
|
||||
import draggable from 'vuedraggable'
|
||||
|
||||
export default {
|
||||
name: 'ApsQuickSheet',
|
||||
components: { draggable },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
saving: false,
|
||||
rows: [],
|
||||
lineOptions: [],
|
||||
lineHistory: [],
|
||||
dirtyIds: new Set(),
|
||||
autoSaveTimer: null,
|
||||
templateKey: 'unified',
|
||||
filter: {
|
||||
range: []
|
||||
},
|
||||
pager: {
|
||||
pageNum: 1,
|
||||
pageSize: 25
|
||||
},
|
||||
columnSettingVisible: false,
|
||||
columnSettingList: [],
|
||||
fixedColumnList: [],
|
||||
normalColumnList: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentTemplate() {
|
||||
return getTemplateByKey(this.templateKey)
|
||||
},
|
||||
flatColumns() {
|
||||
const res = []
|
||||
const loop = (cols) => {
|
||||
;(cols || []).forEach(c => {
|
||||
if (c.children && c.children.length) loop(c.children)
|
||||
else res.push(c)
|
||||
})
|
||||
}
|
||||
loop(this.currentTemplate.columns || [])
|
||||
return res.filter(c => c && c.prop)
|
||||
},
|
||||
displayColumns() {
|
||||
const setting = this.columnSettingList || []
|
||||
const hasSetting = setting.length > 0
|
||||
const base = this.flatColumns
|
||||
|
||||
const selected = hasSetting
|
||||
? setting.filter(i => i.visible).map(i => base.find(c => c.prop === i.prop)).filter(Boolean)
|
||||
: base
|
||||
|
||||
const selectedSet = new Set(selected.map(c => c.prop))
|
||||
const rest = base.filter(c => !selectedSet.has(c.prop))
|
||||
return [...selected, ...rest]
|
||||
},
|
||||
fixedProps() {
|
||||
return (this.columnSettingList || []).filter(i => i.visible && i.fixed).map(i => i.prop).slice(0, 5)
|
||||
},
|
||||
displayRows() {
|
||||
const [startAfter, endBefore] = this.filter.range || []
|
||||
const filtered = this.rows.filter(r => {
|
||||
if (!r.startTime) return true
|
||||
const d = String(r.startTime).slice(0, 10)
|
||||
if (startAfter && d < startAfter) return false
|
||||
if (endBefore && d > endBefore) return false
|
||||
return true
|
||||
})
|
||||
const start = (this.pager.pageNum - 1) * this.pager.pageSize
|
||||
const page = filtered.slice(start, start + this.pager.pageSize)
|
||||
if (page.length < this.pager.pageSize) {
|
||||
return page.concat(this.buildEmptyRows(this.pager.pageSize - page.length))
|
||||
}
|
||||
return page
|
||||
},
|
||||
totalRows() {
|
||||
const [startAfter, endBefore] = this.filter.range || []
|
||||
return this.rows.filter(r => {
|
||||
if (!r.startTime) return true
|
||||
const d = String(r.startTime).slice(0, 10)
|
||||
if (startAfter && d < startAfter) return false
|
||||
if (endBefore && d > endBefore) return false
|
||||
return true
|
||||
}).length
|
||||
},
|
||||
missingRows() {
|
||||
return this.collectMissing(this.displayRows)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const today = new Date()
|
||||
const start = `${today.getFullYear()}-${`${today.getMonth() + 1}`.padStart(2, '0')}-${`${today.getDate()}`.padStart(2, '0')}`
|
||||
this.filter.range = [start, '']
|
||||
this.initColumnSettings()
|
||||
this.loadRows()
|
||||
this.loadLines()
|
||||
window.addEventListener('keydown', this.handleGlobalKeydown)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.autoSaveTimer) clearTimeout(this.autoSaveTimer)
|
||||
this.persistColumnSettings()
|
||||
window.removeEventListener('keydown', this.handleGlobalKeydown)
|
||||
},
|
||||
methods: {
|
||||
handleGlobalKeydown(e) {
|
||||
const isMac = /Mac|iPod|iPhone|iPad/.test(navigator.userAgent)
|
||||
const ctrlOrCmd = isMac ? e.metaKey : e.ctrlKey
|
||||
if (ctrlOrCmd && (e.key === 's' || e.key === 'S')) {
|
||||
e.preventDefault()
|
||||
this.saveAll(false)
|
||||
}
|
||||
},
|
||||
focusNextCell(visibleRowIndex, colIndex, direction) {
|
||||
const pageStart = (this.pager.pageNum - 1) * this.pager.pageSize
|
||||
const targetRow = direction === 'down' ? visibleRowIndex + 1 : visibleRowIndex
|
||||
const targetCol = direction === 'right' ? colIndex + 1 : colIndex
|
||||
if (targetCol >= this.displayColumns.length) return
|
||||
|
||||
const globalRowIndex = pageStart + targetRow
|
||||
if (globalRowIndex >= this.rows.length) {
|
||||
this.rows.push(this.buildEmptyRow())
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
const wrappers = this.$el.querySelectorAll('.excel-table .el-table__body-wrapper tbody tr')
|
||||
const tr = wrappers[targetRow]
|
||||
if (!tr) return
|
||||
const tds = tr.querySelectorAll('td')
|
||||
const td = tds[targetCol + 1]
|
||||
if (!td) return
|
||||
const input = td.querySelector('input, textarea, .el-input__inner')
|
||||
if (input) input.focus()
|
||||
})
|
||||
},
|
||||
async loadLines() {
|
||||
const res = await listProductionLine({ pageNum: 1, pageSize: 1000 })
|
||||
this.lineOptions = res.rows || []
|
||||
},
|
||||
async loadRows() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await fetchQuickSheetList()
|
||||
this.rows = (res.data || []).map(r => this.normalizeRow(r))
|
||||
this.lineHistory = this.rows
|
||||
.map(r => r.lineName)
|
||||
.filter(v => v && v.trim())
|
||||
.filter((v, i, arr) => arr.indexOf(v) === i)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
buildEmptyRows(count) {
|
||||
return Array.from({ length: count }).map(() => this.buildEmptyRow())
|
||||
},
|
||||
async applyPreset(line) {
|
||||
if (!line || !line.lineId) return
|
||||
const res = await fetchQuickSheetPreset({ lineId: line.lineId })
|
||||
const preset = res.data || []
|
||||
if (!preset.length) return
|
||||
const targetRow = this.rows.find(r => !r.lineId && !r.lineName) || this.buildEmptyRow()
|
||||
const base = this.normalizeRow(preset[0])
|
||||
Object.keys(base).forEach(k => {
|
||||
if (k === 'quickSheetId') return
|
||||
targetRow[k] = base[k]
|
||||
})
|
||||
targetRow.lineId = line.lineId
|
||||
targetRow.lineName = line.lineName || line.lineCode || ''
|
||||
if (!this.rows.includes(targetRow)) {
|
||||
this.rows.push(targetRow)
|
||||
}
|
||||
if (this.rows.length < 25) {
|
||||
this.rows = this.rows.concat(this.buildEmptyRows(25 - this.rows.length))
|
||||
}
|
||||
this.markDirty(targetRow)
|
||||
this.scheduleAutoSave()
|
||||
},
|
||||
onCellChange(row) {
|
||||
if (row && !this.rows.includes(row)) {
|
||||
this.rows.push(row)
|
||||
}
|
||||
this.markDirty(row)
|
||||
this.scheduleAutoSave()
|
||||
},
|
||||
queryLineHistory(query, cb) {
|
||||
const list = this.lineHistory || []
|
||||
const q = (query || '').trim().toLowerCase()
|
||||
const res = list
|
||||
.filter(item => !q || String(item).toLowerCase().includes(q))
|
||||
.map(item => ({ value: item }))
|
||||
cb(res)
|
||||
},
|
||||
onLineHistoryInput(row) {
|
||||
if (!row) return
|
||||
if (row.lineId && row.lineName && row.lineName.trim() !== String(row.lineName).trim()) {
|
||||
row.lineId = null
|
||||
}
|
||||
this.onCellChange(row)
|
||||
},
|
||||
onLineHistorySelect(row) {
|
||||
return () => {
|
||||
this.onCellChange(row)
|
||||
}
|
||||
},
|
||||
onFilterChange() {
|
||||
this.pager.pageNum = 1
|
||||
},
|
||||
normalizeRow(row) {
|
||||
const base = this.buildEmptyRow()
|
||||
Object.keys(base).forEach(k => {
|
||||
if (row && row[k] !== undefined && row[k] !== null) base[k] = row[k]
|
||||
})
|
||||
const idVal = row ? (row.quickSheetId !== undefined && row.quickSheetId !== null ? row.quickSheetId : row.id) : null
|
||||
base.quickSheetId = idVal != null ? idVal : null
|
||||
return base
|
||||
},
|
||||
buildEmptyRow() {
|
||||
const row = { quickSheetId: null }
|
||||
this.flatColumns.forEach(col => {
|
||||
if (!col.prop) return
|
||||
row[col.prop] = ''
|
||||
})
|
||||
return row
|
||||
},
|
||||
isRowEmpty(row) {
|
||||
if (!row) return true
|
||||
return !this.flatColumns.some(col => col.prop && row[col.prop])
|
||||
},
|
||||
isTimeField(prop) {
|
||||
return prop === 'startTime' || prop === 'endTime'
|
||||
},
|
||||
isLeadingColumn(col) {
|
||||
if (!col || !col.prop) return false
|
||||
return this.leadingPropsByRole.includes(col.prop)
|
||||
},
|
||||
initColumnSettings() {
|
||||
const key = this.getColumnSettingKey()
|
||||
const stored = localStorage.getItem(key)
|
||||
const base = this.flatColumns.map((col, idx) => ({ prop: col.prop, label: col.label, visible: true, fixed: idx < 5 }))
|
||||
if (!stored) {
|
||||
this.columnSettingList = base
|
||||
this.resetColumnDraft()
|
||||
return
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(stored)
|
||||
if (!Array.isArray(parsed)) {
|
||||
this.columnSettingList = base
|
||||
this.resetColumnDraft()
|
||||
return
|
||||
}
|
||||
const savedProps = parsed.map(i => i.prop)
|
||||
const extras = base.filter(i => !savedProps.includes(i.prop))
|
||||
this.columnSettingList = [...parsed.filter(i => base.some(b => b.prop === i.prop)).map(i => {
|
||||
const match = base.find(b => b.prop === i.prop)
|
||||
return { ...match, visible: i.visible !== false, fixed: i.fixed === true }
|
||||
}), ...extras]
|
||||
this.resetColumnDraft()
|
||||
} catch (e) {
|
||||
this.columnSettingList = base
|
||||
this.resetColumnDraft()
|
||||
}
|
||||
},
|
||||
resetColumnDraft() {
|
||||
const fixed = (this.columnSettingList || []).filter(i => i.visible && i.fixed).slice(0, 5)
|
||||
const normal = (this.columnSettingList || []).filter(i => !i.fixed || !i.visible)
|
||||
this.fixedColumnList = fixed.map(i => ({ ...i, visible: true, fixed: true }))
|
||||
this.normalColumnList = normal.map(i => ({ ...i, fixed: false }))
|
||||
},
|
||||
saveColumnSettings() {
|
||||
const fixed = (this.fixedColumnList || []).map(i => ({ ...i, visible: true, fixed: true }))
|
||||
const normal = (this.normalColumnList || []).map(i => ({ ...i, fixed: false }))
|
||||
this.columnSettingList = [...fixed, ...normal]
|
||||
this.persistColumnSettings()
|
||||
this.columnSettingVisible = false
|
||||
this.pager.pageNum = 1
|
||||
this.loadRows().finally(() => {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.quickSheetTable && this.$refs.quickSheetTable.doLayout && this.$refs.quickSheetTable.doLayout()
|
||||
})
|
||||
})
|
||||
},
|
||||
addToFixed(prop) {
|
||||
if ((this.fixedColumnList || []).length >= 5) {
|
||||
this.$message.warning('固定列最多5列')
|
||||
return
|
||||
}
|
||||
const idx = this.normalColumnList.findIndex(i => i.prop === prop)
|
||||
if (idx < 0) return
|
||||
const item = this.normalColumnList.splice(idx, 1)[0]
|
||||
this.fixedColumnList.push({ ...item, visible: true, fixed: true })
|
||||
},
|
||||
removeFromFixed(prop) {
|
||||
const idx = this.fixedColumnList.findIndex(i => i.prop === prop)
|
||||
if (idx < 0) return
|
||||
const item = this.fixedColumnList.splice(idx, 1)[0]
|
||||
this.normalColumnList.unshift({ ...item, fixed: false })
|
||||
},
|
||||
persistColumnSettings() {
|
||||
const key = this.getColumnSettingKey()
|
||||
const payload = (this.columnSettingList || []).map(item => ({
|
||||
prop: item.prop,
|
||||
label: item.label,
|
||||
visible: item.visible !== false,
|
||||
fixed: item.fixed === true
|
||||
}))
|
||||
localStorage.setItem(key, JSON.stringify(payload))
|
||||
},
|
||||
restoreDefaultColumns() {
|
||||
const base = this.flatColumns.map((col, idx) => ({ prop: col.prop, label: col.label, visible: true, fixed: idx < 5 }))
|
||||
this.columnSettingList = base
|
||||
this.resetColumnDraft()
|
||||
},
|
||||
getColumnSettingKey() {
|
||||
return `apsQuickSheetColumns_${this.templateKey}`
|
||||
},
|
||||
markDirty(row) {
|
||||
if (!row) return
|
||||
if (!row._tmpId) row._tmpId = Math.random().toString(36).slice(2)
|
||||
this.dirtyIds.add(row._tmpId)
|
||||
},
|
||||
async clearRow(row) {
|
||||
if (!row) return
|
||||
if (!row.quickSheetId) {
|
||||
this.flatColumns.forEach(col => {
|
||||
if (!col.prop) return
|
||||
row[col.prop] = ''
|
||||
})
|
||||
row.quickSheetId = null
|
||||
return
|
||||
}
|
||||
await deleteQuickSheetRow(row.quickSheetId)
|
||||
this.flatColumns.forEach(col => {
|
||||
if (!col.prop) return
|
||||
row[col.prop] = ''
|
||||
})
|
||||
row.quickSheetId = null
|
||||
},
|
||||
scheduleAutoSave() {
|
||||
if (this.autoSaveTimer) clearTimeout(this.autoSaveTimer)
|
||||
this.autoSaveTimer = setTimeout(() => {
|
||||
this.saveAll(true)
|
||||
}, 800)
|
||||
},
|
||||
async saveAll(isAuto = false, payload) {
|
||||
const rows = payload && payload.rows
|
||||
? payload.rows
|
||||
: this.rows.filter(r => r && r._tmpId && this.dirtyIds.has(r._tmpId))
|
||||
.map(r => ({
|
||||
...r,
|
||||
quickSheetId: r.quickSheetId || null
|
||||
}))
|
||||
if (!rows.length) {
|
||||
if (!isAuto) this.$message.warning('暂无可保存数据')
|
||||
return
|
||||
}
|
||||
const missing = this.collectMissing(rows)
|
||||
if (missing.length && !isAuto) {
|
||||
this.$message.warning(`必填项未填写:${missing.join(';')}`)
|
||||
return
|
||||
}
|
||||
this.saving = true
|
||||
try {
|
||||
await saveQuickSheet({ rows })
|
||||
if (!isAuto) this.$message.success('保存成功')
|
||||
rows.forEach(r => {
|
||||
if (r._tmpId) this.dirtyIds.delete(r._tmpId)
|
||||
})
|
||||
} finally {
|
||||
this.saving = false
|
||||
}
|
||||
},
|
||||
exportCsv() {
|
||||
this.loading = true
|
||||
exportQuickSheet()
|
||||
.then(blob => {
|
||||
const filename = `quick_sheet_${new Date().getTime()}.xlsx`
|
||||
saveAs(new Blob([blob], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }), filename)
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
addRow() {
|
||||
this.rows.unshift(this.buildEmptyRow())
|
||||
this.pager.pageNum = 1
|
||||
},
|
||||
collectMissing(rows) {
|
||||
const msgs = []
|
||||
rows.forEach((row, idx) => {
|
||||
if (this.isRowEmpty(row)) return
|
||||
const line = idx + 1
|
||||
const missing = []
|
||||
if (!row.lineName && !row.lineId) missing.push('产线')
|
||||
if (!row.planCode) missing.push('计划号')
|
||||
if (!row.startTime) missing.push('开始时间')
|
||||
if (!row.endTime) missing.push('结束时间')
|
||||
if (missing.length) {
|
||||
msgs.push(`第${line}行缺少${missing.join('、')}`)
|
||||
}
|
||||
})
|
||||
return msgs
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.aps-quick-sheet {
|
||||
padding: 8px;
|
||||
background: #f7f9fc;
|
||||
}
|
||||
|
||||
.sheet-toolbar,
|
||||
.sheet-body {
|
||||
border: 1px solid #eef2f7;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.sheet-toolbar {
|
||||
margin-bottom: 8px;
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.sheet-actions { display: flex; gap: 8px; }
|
||||
|
||||
.preset-bar { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #667085; }
|
||||
.preset-title { font-weight: 600; color: #475467; }
|
||||
|
||||
::v-deep .excel-table {
|
||||
border: 1px solid #edf1f7;
|
||||
}
|
||||
::v-deep .excel-table th.el-table__cell {
|
||||
background: #fafbfe;
|
||||
color: #5d6b82;
|
||||
font-weight: 600;
|
||||
border-right: 1px solid #edf1f7;
|
||||
border-bottom: 1px solid #edf1f7;
|
||||
padding: 4px 0;
|
||||
}
|
||||
::v-deep .excel-table td.el-table__cell {
|
||||
border-right: 1px solid #f0f2f6;
|
||||
border-bottom: 1px solid #f0f2f6;
|
||||
padding: 1px 2px;
|
||||
}
|
||||
::v-deep .excel-table .el-table__row:hover > td {
|
||||
background-color: #f7faff !important;
|
||||
}
|
||||
|
||||
::v-deep .el-input__inner {
|
||||
border-radius: 0;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
::v-deep .el-input__inner:focus {
|
||||
border: none;
|
||||
box-shadow: inset 0 0 0 1px #4a90e2;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.missing-tip {
|
||||
margin-top: 8px;
|
||||
padding: 8px 12px;
|
||||
background: #fff7e6;
|
||||
border: 1px solid #ffd591;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
color: #d46b08;
|
||||
}
|
||||
|
||||
.missing-item {
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.col-setting-body {
|
||||
padding: 12px;
|
||||
height: calc(100% - 12px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.col-setting-toolbar {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.col-section {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.col-section-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.col-setting-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.col-setting-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
margin-bottom: 0;
|
||||
min-height: 38px;
|
||||
}
|
||||
|
||||
.col-setting-item:hover {
|
||||
border-color: #d9ecff;
|
||||
background: #f5faff;
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
cursor: move;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,428 +0,0 @@
|
||||
<template>
|
||||
<div class="aps-quick-sheet-preview excel-theme">
|
||||
<div class="sheet-toolbar">
|
||||
<div class="sheet-actions">
|
||||
<el-date-picker
|
||||
v-model="filter.range"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="yyyy-MM-dd"
|
||||
size="small"
|
||||
style="width: 240px"
|
||||
@change="onFilterChange"
|
||||
/>
|
||||
<el-select v-model="filter.lineId" clearable filterable size="small" placeholder="产线" style="width: 160px" @change="onFilterChange">
|
||||
<el-option v-for="line in lineOptions" :key="line.lineId" :label="line.lineName || line.lineCode || ('产线' + line.lineId)" :value="line.lineId" />
|
||||
</el-select>
|
||||
<el-input v-model="filter.customer" size="small" clearable placeholder="客户" style="width: 160px" @input="onFilterChange" />
|
||||
<el-button size="small" icon="el-icon-refresh" @click="loadRows">刷新</el-button>
|
||||
<el-button size="small" icon="el-icon-setting" @click="columnSettingVisible = true">列设置</el-button>
|
||||
<el-button size="small" icon="el-icon-download" @click="exportExcel">导出</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sheet-body">
|
||||
<el-table
|
||||
ref="quickSheetPreviewTable"
|
||||
v-loading="loading"
|
||||
:data="displayRows"
|
||||
border
|
||||
size="mini"
|
||||
class="excel-table"
|
||||
>
|
||||
<el-table-column label="序号" width="60" align="center" fixed="left">
|
||||
<template slot-scope="scope">
|
||||
{{ ((pager.pageNum - 1) * pager.pageSize) + scope.$index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-for="col in displayColumns"
|
||||
:key="col.prop || col.label"
|
||||
:label="col.label"
|
||||
:prop="col.prop"
|
||||
:width="col.width"
|
||||
:min-width="col.minWidth"
|
||||
:align="col.align || 'center'"
|
||||
:fixed="fixedProps.includes(col.prop) ? 'left' : false"
|
||||
:show-overflow-tooltip="col.showOverflowTooltip !== false"
|
||||
/>
|
||||
</el-table>
|
||||
<div class="pager-wrap">
|
||||
<el-pagination
|
||||
small
|
||||
background
|
||||
layout="prev, pager, next, total"
|
||||
:current-page.sync="pager.pageNum"
|
||||
:page-size="pager.pageSize"
|
||||
:total="totalRows"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-drawer
|
||||
title="列设置"
|
||||
:visible.sync="columnSettingVisible"
|
||||
direction="rtl"
|
||||
size="55%"
|
||||
append-to-body
|
||||
@open="resetColumnDraft">
|
||||
<div class="col-setting-body">
|
||||
<div class="col-setting-toolbar">
|
||||
<el-button size="mini" @click="restoreDefaultColumns">恢复默认</el-button>
|
||||
<el-button size="mini" @click="resetColumnDraft">重置</el-button>
|
||||
<el-button size="mini" type="primary" @click="saveColumnSettings">保存</el-button>
|
||||
</div>
|
||||
|
||||
<div class="col-section">
|
||||
<div class="col-section-title">固定列(最多5列)</div>
|
||||
<draggable v-model="fixedColumnList" handle=".drag-handle" animation="200" class="col-setting-grid">
|
||||
<div v-for="item in fixedColumnList" :key="`fixed-${item.prop}`" class="col-setting-item">
|
||||
<i class="el-icon-rank drag-handle" />
|
||||
<span class="col-label">{{ item.label }}</span>
|
||||
<el-button type="text" size="mini" @click="removeFromFixed(item.prop)">移除</el-button>
|
||||
</div>
|
||||
</draggable>
|
||||
</div>
|
||||
|
||||
<div class="col-section">
|
||||
<div class="col-section-title">其他列</div>
|
||||
<draggable v-model="normalColumnList" handle=".drag-handle" animation="200" class="col-setting-grid">
|
||||
<div v-for="item in normalColumnList" :key="`normal-${item.prop}`" class="col-setting-item">
|
||||
<i class="el-icon-rank drag-handle" />
|
||||
<span class="col-label">{{ item.label }}</span>
|
||||
<el-button type="text" size="mini" @click="addToFixed(item.prop)">加入固定</el-button>
|
||||
</div>
|
||||
</draggable>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fetchQuickSheetList, exportQuickSheet } from '@/api/aps/quickSheet'
|
||||
import { listProductionLine } from '@/api/wms/productionLine'
|
||||
import { getTemplateByKey } from './sheets/templates'
|
||||
import { saveAs } from 'file-saver'
|
||||
import draggable from 'vuedraggable'
|
||||
|
||||
export default {
|
||||
name: 'ApsQuickSheetPreview',
|
||||
components: { draggable },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
rows: [],
|
||||
lineOptions: [],
|
||||
filter: {
|
||||
range: [],
|
||||
lineId: null,
|
||||
customer: ''
|
||||
},
|
||||
pager: {
|
||||
pageNum: 1,
|
||||
pageSize: 25
|
||||
},
|
||||
columnSettingVisible: false,
|
||||
columnSettingList: [],
|
||||
fixedColumnList: [],
|
||||
normalColumnList: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentTemplate() {
|
||||
return getTemplateByKey('unified')
|
||||
},
|
||||
flatColumns() {
|
||||
const res = []
|
||||
const loop = (cols) => {
|
||||
;(cols || []).forEach(c => {
|
||||
if (c.children && c.children.length) loop(c.children)
|
||||
else res.push(c)
|
||||
})
|
||||
}
|
||||
loop(this.currentTemplate.columns || [])
|
||||
return res.filter(c => c && c.prop)
|
||||
},
|
||||
displayColumns() {
|
||||
const setting = this.columnSettingList || []
|
||||
const hasSetting = setting.length > 0
|
||||
const base = this.flatColumns
|
||||
|
||||
const selected = hasSetting
|
||||
? setting.filter(i => i.visible).map(i => base.find(c => c.prop === i.prop)).filter(Boolean)
|
||||
: base
|
||||
|
||||
const selectedSet = new Set(selected.map(c => c.prop))
|
||||
const rest = base.filter(c => !selectedSet.has(c.prop))
|
||||
return [...selected, ...rest]
|
||||
},
|
||||
fixedProps() {
|
||||
return (this.columnSettingList || []).filter(i => i.visible && i.fixed).map(i => i.prop).slice(0, 5)
|
||||
},
|
||||
filteredRows() {
|
||||
const [startAfter, endBefore] = this.filter.range || []
|
||||
const lineName = (this.filter.lineName || '').trim()
|
||||
const customer = (this.filter.customer || '').trim().toLowerCase()
|
||||
return this.rows.filter(r => {
|
||||
if (startAfter || endBefore) {
|
||||
const d = r.startTime ? String(r.startTime).slice(0, 10) : ''
|
||||
if (startAfter && d && d < startAfter) return false
|
||||
if (endBefore && d && d > endBefore) return false
|
||||
}
|
||||
if (lineName && r.lineName !== lineName) return false
|
||||
if (customer) {
|
||||
const txt = String(r.customerName || '').toLowerCase()
|
||||
if (!txt.includes(customer)) return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
},
|
||||
displayRows() {
|
||||
const start = (this.pager.pageNum - 1) * this.pager.pageSize
|
||||
return this.filteredRows.slice(start, start + this.pager.pageSize)
|
||||
},
|
||||
totalRows() {
|
||||
return this.filteredRows.length
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const today = new Date()
|
||||
const start = `${today.getFullYear()}-${`${today.getMonth() + 1}`.padStart(2, '0')}-${`${today.getDate()}`.padStart(2, '0')}`
|
||||
this.filter.range = [start, '']
|
||||
this.initColumnSettings()
|
||||
this.loadRows()
|
||||
this.loadLines()
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.persistColumnSettings()
|
||||
},
|
||||
methods: {
|
||||
initColumnSettings() {
|
||||
const key = this.getColumnSettingKey()
|
||||
const stored = localStorage.getItem(key)
|
||||
const base = this.flatColumns.map((col, idx) => ({ prop: col.prop, label: col.label, visible: true, fixed: idx < 5 }))
|
||||
if (!stored) {
|
||||
this.columnSettingList = base
|
||||
return
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(stored)
|
||||
if (!Array.isArray(parsed)) {
|
||||
this.columnSettingList = base
|
||||
return
|
||||
}
|
||||
const map = new Map(parsed.map(item => [item.prop, item]))
|
||||
const merged = base.map(item => {
|
||||
const saved = map.get(item.prop)
|
||||
return saved ? { ...item, visible: saved.visible !== false } : item
|
||||
})
|
||||
const savedProps = parsed.map(i => i.prop)
|
||||
const extras = base.filter(i => !savedProps.includes(i.prop))
|
||||
this.columnSettingList = [...parsed.filter(i => base.some(b => b.prop === i.prop)).map(i => {
|
||||
const match = base.find(b => b.prop === i.prop)
|
||||
return { ...match, visible: i.visible !== false, fixed: i.fixed === true }
|
||||
}), ...extras]
|
||||
this.resetColumnDraft()
|
||||
} catch (e) {
|
||||
this.columnSettingList = base
|
||||
this.resetColumnDraft()
|
||||
}
|
||||
},
|
||||
resetColumnDraft() {
|
||||
const fixed = (this.columnSettingList || []).filter(i => i.visible && i.fixed).slice(0, 5)
|
||||
const normal = (this.columnSettingList || []).filter(i => !i.fixed || !i.visible)
|
||||
this.fixedColumnList = fixed.map(i => ({ ...i, visible: true, fixed: true }))
|
||||
this.normalColumnList = normal.map(i => ({ ...i, visible: true, fixed: false }))
|
||||
},
|
||||
saveColumnSettings() {
|
||||
const fixed = (this.fixedColumnList || []).map(i => ({ ...i, visible: true, fixed: true }))
|
||||
const normal = (this.normalColumnList || []).map(i => ({ ...i, fixed: false }))
|
||||
this.columnSettingList = [...fixed, ...normal]
|
||||
this.persistColumnSettings()
|
||||
this.columnSettingVisible = false
|
||||
this.pager.pageNum = 1
|
||||
this.loadRows().finally(() => {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.quickSheetPreviewTable && this.$refs.quickSheetPreviewTable.doLayout && this.$refs.quickSheetPreviewTable.doLayout()
|
||||
})
|
||||
})
|
||||
},
|
||||
addToFixed(prop) {
|
||||
if ((this.fixedColumnList || []).length >= 5) {
|
||||
this.$message.warning('固定列最多5列')
|
||||
return
|
||||
}
|
||||
const idx = (this.normalColumnList || []).findIndex(i => i.prop === prop)
|
||||
if (idx < 0) return
|
||||
const item = this.normalColumnList.splice(idx, 1)[0]
|
||||
this.fixedColumnList.push({ ...item, fixed: true, visible: true })
|
||||
},
|
||||
removeFromFixed(prop) {
|
||||
const idx = (this.fixedColumnList || []).findIndex(i => i.prop === prop)
|
||||
if (idx < 0) return
|
||||
const item = this.fixedColumnList.splice(idx, 1)[0]
|
||||
this.normalColumnList.push({ ...item, fixed: false, visible: true })
|
||||
},
|
||||
persistColumnSettings() {
|
||||
const key = this.getColumnSettingKey()
|
||||
const payload = (this.columnSettingList || []).map(item => ({
|
||||
prop: item.prop,
|
||||
label: item.label,
|
||||
visible: item.visible !== false,
|
||||
fixed: item.fixed === true
|
||||
}))
|
||||
localStorage.setItem(key, JSON.stringify(payload))
|
||||
},
|
||||
restoreDefaultColumns() {
|
||||
const base = this.flatColumns.map((col, idx) => ({ prop: col.prop, label: col.label, visible: true, fixed: idx < 5 }))
|
||||
this.columnSettingList = base
|
||||
this.resetColumnDraft()
|
||||
this.persistColumnSettings()
|
||||
},
|
||||
getColumnSettingKey() {
|
||||
return 'apsQuickSheetPreviewColumns_unified'
|
||||
},
|
||||
async loadLines() {
|
||||
const res = await listProductionLine({ pageNum: 1, pageSize: 1000 })
|
||||
this.lineOptions = res.rows || []
|
||||
},
|
||||
async loadRows() {
|
||||
this.loading = true
|
||||
try {
|
||||
const params = this.buildQueryParams()
|
||||
const res = await fetchQuickSheetList(params)
|
||||
this.rows = (res.data || []).map(r => ({ ...r }))
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
onFilterChange() {
|
||||
this.pager.pageNum = 1
|
||||
this.loadRows()
|
||||
},
|
||||
exportExcel() {
|
||||
const params = this.buildQueryParams()
|
||||
exportQuickSheet(params)
|
||||
.then(blob => {
|
||||
const filename = `quick_sheet_preview_${new Date().getTime()}.xlsx`
|
||||
saveAs(new Blob([blob], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }), filename)
|
||||
})
|
||||
},
|
||||
buildQueryParams() {
|
||||
const [startDate, endDate] = this.filter.range || []
|
||||
return {
|
||||
startDate: startDate || undefined,
|
||||
endDate: endDate || undefined,
|
||||
lineId: this.filter.lineId || undefined,
|
||||
customerName: this.filter.customer || undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.aps-quick-sheet-preview {
|
||||
padding: 8px;
|
||||
background: #f7f9fc;
|
||||
}
|
||||
|
||||
.sheet-toolbar {
|
||||
margin-bottom: 8px;
|
||||
padding: 8px;
|
||||
border: 1px solid #eef2f7;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.sheet-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.sheet-body {
|
||||
border: 1px solid #eef2f7;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
::v-deep .excel-table {
|
||||
border: 1px solid #edf1f7;
|
||||
}
|
||||
::v-deep .excel-table th.el-table__cell {
|
||||
background: #fafbfe;
|
||||
color: #5d6b82;
|
||||
font-weight: 600;
|
||||
border-right: 1px solid #edf1f7;
|
||||
border-bottom: 1px solid #edf1f7;
|
||||
padding: 4px 0;
|
||||
}
|
||||
::v-deep .excel-table td.el-table__cell {
|
||||
border-right: 1px solid #f0f2f6;
|
||||
border-bottom: 1px solid #f0f2f6;
|
||||
padding: 1px 2px;
|
||||
}
|
||||
|
||||
.pager-wrap {
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.col-setting-body {
|
||||
padding: 12px;
|
||||
height: calc(100% - 12px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.col-setting-toolbar {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.col-section {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.col-section-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.col-setting-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.col-setting-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
margin-bottom: 0;
|
||||
min-height: 38px;
|
||||
}
|
||||
|
||||
.col-setting-item:hover {
|
||||
border-color: #d9ecff;
|
||||
background: #f5faff;
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
cursor: move;
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
@@ -15,48 +15,22 @@ export const APS_SHEET_TEMPLATES = [
|
||||
key: 'unified',
|
||||
name: '统一排产表',
|
||||
columns: [
|
||||
{ label: '产线', prop: 'lineName', minWidth: 140 },
|
||||
{ label: '排产单号', prop: 'planCode', minWidth: 140 },
|
||||
{ label: '排产类型', prop: 'planType', width: 100 },
|
||||
{ label: '排产人', prop: 'scheduler', width: 100 },
|
||||
{ label: '修改人', prop: 'updateBy', width: 100 },
|
||||
{ label: '备注', prop: 'remark', minWidth: 160 },
|
||||
|
||||
{ label: '内容序号', prop: 'bizSeqNo', width: 110 },
|
||||
{ label: '销售内容', prop: 'orderCode', minWidth: 140 },
|
||||
{ label: '订单合同号', prop: 'contractCode', minWidth: 140 },
|
||||
{ label: '产线', prop: 'lineName', minWidth: 120 },
|
||||
{ label: '计划号', prop: 'planCode', minWidth: 140 },
|
||||
{ label: '订单号', prop: 'orderCode', minWidth: 140 },
|
||||
{ label: '客户', prop: 'customerName', minWidth: 140 },
|
||||
{ label: '业务员', prop: 'salesman', width: 100 },
|
||||
|
||||
{ label: '成品名称', prop: 'productName', minWidth: 140 },
|
||||
{ label: '材质', prop: 'productMaterial', width: 100 },
|
||||
{ label: '镀层g', prop: 'coatingG', width: 90, align: 'right' },
|
||||
{ label: '成品宽度', prop: 'productWidth', width: 100, align: 'right' },
|
||||
{ label: '扎制厚度', prop: 'rollingThick', width: 100, align: 'right' },
|
||||
{ label: '标丝厚度', prop: 'markCoatThick', width: 100, align: 'right' },
|
||||
{ label: '吨钢长度区间m', prop: 'tonSteelLengthRange', minWidth: 130 },
|
||||
{ label: '数量', prop: 'planQty', width: 90, align: 'right' },
|
||||
{ label: '重量', prop: 'planWeight', width: 100, align: 'right' },
|
||||
{ label: '表面处理', prop: 'surfaceTreatment', width: 110 },
|
||||
{ label: '切边要求', prop: 'widthReq', width: 110 },
|
||||
{ label: '宽度要求', prop: 'rawEdgeReq', width: 110 },
|
||||
{ label: '用途', prop: 'usageReq', width: 100 },
|
||||
{ label: '后处理', prop: 'postProcess', width: 100 },
|
||||
{ label: '下工序', prop: 'nextProcess', width: 100 },
|
||||
{ label: '取样', prop: 'sampleReq', width: 90 },
|
||||
{ label: '开始时间', prop: 'startTime', width: 170 },
|
||||
{ label: '结束时间', prop: 'endTime', width: 170 },
|
||||
|
||||
{ label: '厂家', prop: 'rawManufacturer', width: 100 },
|
||||
{ label: '原料信息', prop: 'rawMaterial', minWidth: 140 },
|
||||
{ label: '原料厚度mm', prop: 'rawThick', width: 110, align: 'right' },
|
||||
{ label: '宽度mm', prop: 'rawWidth', width: 100, align: 'right' },
|
||||
{ label: '产品', prop: 'productName', minWidth: 140 },
|
||||
{ label: '原料钢卷', prop: 'rawMaterialId', minWidth: 220 },
|
||||
{ label: '原料卷号', prop: 'rawCoilNos', minWidth: 220 },
|
||||
{ label: '钢卷位置', prop: 'rawLocation', minWidth: 140 },
|
||||
{ label: '包装要求', prop: 'rawPackaging', minWidth: 140 },
|
||||
{ label: '切边要求', prop: 'rawEdgeReq', minWidth: 120 },
|
||||
{ label: '镀层种类', prop: 'rawCoatingType', width: 110 },
|
||||
{ label: '原料净重', prop: 'rawNetWeight', width: 110, align: 'right' }
|
||||
{ label: '原料净重', prop: 'rawNetWeight', width: 110, align: 'right' },
|
||||
{ label: '计划数量', prop: 'planQty', width: 100, align: 'right' },
|
||||
{ label: '开始时间', prop: 'startTime', width: 170 },
|
||||
{ label: '结束时间', prop: 'endTime', width: 170 }
|
||||
],
|
||||
summary: { sumFields: ['planQty', 'rawNetWeight'] }
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<img :src="avatar" class="user-avatar" alt="头像" />
|
||||
<div class="greeting-text">
|
||||
<div class="greeting-title">{{ greeting }},{{ name }}</div>
|
||||
<div class="greeting-desc">欢迎使用科伦普冷轧涂渡数智一体化平台</div>
|
||||
<div class="greeting-desc">欢迎使用MES数智一体化平台</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -12,10 +12,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="oee-query-bar">
|
||||
<el-select v-model="lineType" size="small" style="width: 120px">
|
||||
<el-option label="酸轧线" value="acid" />
|
||||
<el-option label="镀锌一线" value="galvanize1" />
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-model="queryRange"
|
||||
type="daterange"
|
||||
@@ -52,15 +48,6 @@
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-alert
|
||||
v-if="lineType === 'galvanize1'"
|
||||
title="镀锌二级数据未完全使用,故而停机时间可能有错误"
|
||||
type="warning"
|
||||
:closable="false"
|
||||
class="oee-top-warning"
|
||||
show-icon
|
||||
/>
|
||||
|
||||
<el-row :gutter="16" class="oee-main-row">
|
||||
<!-- 左侧:报表主体(Word 风格) -->
|
||||
<el-col :span="18">
|
||||
@@ -92,26 +79,11 @@
|
||||
<span>{{ formatPercent(scope.row.performanceTon) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="quality" label="良品率(%)" align="center">
|
||||
<el-table-column prop="quality" label="良品率 Q (%)" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatPercent(scope.row.quality) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="qualifiedRate" label="合格品率(%)" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatPercent(scope.row.qualifiedRate) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="defectRate" label="次品率 (%)" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatPercent(scope.row.defectRate) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="pendingRate" label="待判级率 (%)" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatPercent(scope.row.pendingRate) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="loadingTimeMin" label="负荷时间 (min)" align="center" />
|
||||
<el-table-column prop="downtimeMin" label="停机时间 (min)" align="center" />
|
||||
<el-table-column prop="runTimeMin" label="运转时间 (min)" align="center" />
|
||||
@@ -121,26 +93,16 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="totalOutputCoil" label="总产量 (卷)" align="center" />
|
||||
<el-table-column prop="goodOutputTon" label="良品(吨)" align="center">
|
||||
<el-table-column prop="goodOutputTon" label="良品量 (吨)" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatNumber(scope.row.goodOutputTon) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="abOutputTon" label="合格(吨)" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatNumber(scope.row.abOutputTon) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="defectOutputTon" label="次品(吨)" align="center">
|
||||
<el-table-column prop="defectOutputTon" label="次品量 (吨)" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatNumber(scope.row.defectOutputTon) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="pendingOutputTon" label="待判(吨)" align="center">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ formatNumber(scope.row.pendingOutputTon) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 日明细(趋势表格风格,方便导出 Word) -->
|
||||
@@ -171,26 +133,11 @@
|
||||
{{ formatPercent(scope.row.performanceTon) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="quality" label="良品率 (%)">
|
||||
<el-table-column prop="quality" label="Q (%)">
|
||||
<template slot-scope="scope">
|
||||
{{ formatPercent(scope.row.quality) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="qualifiedRate" label="合格品率 (%)">
|
||||
<template slot-scope="scope">
|
||||
{{ formatPercent(scope.row.qualifiedRate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="defectRate" label="次品率 (%)">
|
||||
<template slot-scope="scope">
|
||||
{{ formatPercent(scope.row.defectRate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="pendingRate" label="待判级率 (%)">
|
||||
<template slot-scope="scope">
|
||||
{{ formatPercent(scope.row.pendingRate) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="loadingTimeMin" label="负荷 (min)"/>
|
||||
<el-table-column prop="downtimeMin" label="停机 (min)"/>
|
||||
<el-table-column prop="runTimeMin" label="运转 (min)"/>
|
||||
@@ -200,26 +147,16 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="totalOutputCoil" label="总产量 (卷)" />
|
||||
<el-table-column prop="goodOutputTon" label="良品(吨)">
|
||||
<el-table-column prop="goodOutputTon" label="良品 (吨)">
|
||||
<template slot-scope="scope">
|
||||
{{ formatNumber(scope.row.goodOutputTon) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="abOutputTon" label="合格(吨)">
|
||||
<template slot-scope="scope">
|
||||
{{ formatNumber(scope.row.abOutputTon) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="defectOutputTon" label="次品(吨)">
|
||||
<el-table-column prop="defectOutputTon" label="次品 (吨)">
|
||||
<template slot-scope="scope">
|
||||
{{ formatNumber(scope.row.defectOutputTon) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="pendingOutputTon" label="待判(吨)">
|
||||
<template slot-scope="scope">
|
||||
{{ formatNumber(scope.row.pendingOutputTon) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- OEE/A/P/Q 趋势图 -->
|
||||
@@ -326,7 +263,7 @@
|
||||
<ul class="formula-list">
|
||||
<li>A(时间稼动率) = (负荷时间 − 停机时间) / 负荷时间</li>
|
||||
<li>P(性能稼动率,吨维度) = (理论节拍 × 产量吨) / 实际运转时间</li>
|
||||
<li>Q(良品率) = A系列吨 / 总产量吨</li>
|
||||
<li>Q(良品率) = 良品吨 / 总产量吨</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -336,9 +273,8 @@
|
||||
<li><b>负荷时间</b>:计划生产时间扣除计划停机后的时间</li>
|
||||
<li><b>停机时间</b>:所有停机/中断(按 stop_type 汇总)的总时长</li>
|
||||
<li><b>实际运转时间</b>:负荷时间 − 停机时间</li>
|
||||
<li><b>理论节拍</b>:使用整体酸轧库中位数学习得出</li>
|
||||
<li><b>A系列</b>:良品;<b>B系列</b>:合格品;<b>C/D系列</b>:次品;<b>O系列</b>:待判级</li>
|
||||
<li><b>合格品率(AB)</b> = (A+B) / 总量;<b>次品率(CD)</b> = CD / 总量;<b>待判级率(O)</b> = O / 总量</li>
|
||||
<li><b>理论节拍</b>:按“优良日统计口径”得到的稳定节拍(分钟/吨),由理论节拍接口提供</li>
|
||||
<li><b>良品/次品</b>:按 WMS `quality_status` 判断,C+/C/C-/D+/D/D- 为次品</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -396,13 +332,12 @@ export default {
|
||||
const today = new Date()
|
||||
const firstDay = new Date(today.getFullYear(), today.getMonth(), 1)
|
||||
return {
|
||||
lineType: 'acid',
|
||||
queryRange: [
|
||||
this.formatDate(firstDay),
|
||||
this.formatDate(today)
|
||||
],
|
||||
pickerOptions: {
|
||||
disabledDate() {
|
||||
disabledDate(time) {
|
||||
// 不限制选择范围,保留扩展空间
|
||||
return false
|
||||
}
|
||||
@@ -430,18 +365,13 @@ export default {
|
||||
availability: 0,
|
||||
performanceTon: 0,
|
||||
quality: 0,
|
||||
qualifiedRate: 0,
|
||||
defectRate: 0,
|
||||
pendingRate: 0,
|
||||
loadingTimeMin: 0,
|
||||
downtimeMin: 0,
|
||||
runTimeMin: 0,
|
||||
totalOutputTon: 0,
|
||||
totalOutputCoil: 0,
|
||||
goodOutputTon: 0,
|
||||
abOutputTon: 0,
|
||||
defectOutputTon: 0,
|
||||
pendingOutputTon: 0
|
||||
defectOutputTon: 0
|
||||
}
|
||||
}
|
||||
let sumLoading = 0
|
||||
@@ -449,17 +379,12 @@ export default {
|
||||
let sumRun = 0
|
||||
let sumTotalTon = 0
|
||||
let sumGoodTon = 0
|
||||
let sumAbTon = 0
|
||||
let sumDefectTon = 0
|
||||
let sumPendingTon = 0
|
||||
let sumCoil = 0
|
||||
let sumOee = 0
|
||||
let sumA = 0
|
||||
let sumP = 0
|
||||
let sumQ = 0
|
||||
let sumQualifiedRate = 0
|
||||
let sumDefectRate = 0
|
||||
let sumPendingRate = 0
|
||||
|
||||
list.forEach(row => {
|
||||
sumLoading += row.loadingTimeMin || 0
|
||||
@@ -467,37 +392,28 @@ export default {
|
||||
sumRun += row.runTimeMin || 0
|
||||
sumTotalTon += Number(row.totalOutputTon || 0)
|
||||
sumGoodTon += Number(row.goodOutputTon || 0)
|
||||
sumAbTon += Number(row.abOutputTon || 0)
|
||||
sumDefectTon += Number(row.defectOutputTon || 0)
|
||||
sumPendingTon += Number(row.pendingOutputTon || 0)
|
||||
sumCoil += row.totalOutputCoil || 0
|
||||
sumOee += Number(row.oee || 0)
|
||||
sumA += Number(row.availability || 0)
|
||||
sumP += Number(row.performanceTon || 0)
|
||||
sumQ += Number(row.quality || 0)
|
||||
sumQualifiedRate += Number(row.qualifiedRate || 0)
|
||||
sumDefectRate += Number(row.defectRate || 0)
|
||||
sumPendingRate += Number(row.pendingRate || 0)
|
||||
})
|
||||
|
||||
const n = list.length
|
||||
const defectAgg = Math.max(0, sumTotalTon - sumGoodTon)
|
||||
return {
|
||||
oee: n ? sumOee / n : 0,
|
||||
availability: n ? sumA / n : 0,
|
||||
performanceTon: n ? sumP / n : 0,
|
||||
quality: n ? sumQ / n : 0,
|
||||
qualifiedRate: n ? sumQualifiedRate / n : 0,
|
||||
defectRate: n ? sumDefectRate / n : 0,
|
||||
pendingRate: n ? sumPendingRate / n : 0,
|
||||
loadingTimeMin: sumLoading,
|
||||
downtimeMin: sumDowntime,
|
||||
runTimeMin: sumRun,
|
||||
totalOutputTon: sumTotalTon,
|
||||
totalOutputCoil: sumCoil,
|
||||
goodOutputTon: sumGoodTon,
|
||||
abOutputTon: sumAbTon,
|
||||
defectOutputTon: sumDefectTon,
|
||||
pendingOutputTon: sumPendingTon
|
||||
defectOutputTon: defectAgg || sumDefectTon
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -556,7 +472,6 @@ export default {
|
||||
buildQuery() {
|
||||
const [start, end] = this.queryRange || []
|
||||
return {
|
||||
lineType: this.lineType,
|
||||
startDate: start,
|
||||
endDate: end
|
||||
}
|
||||
@@ -574,9 +489,9 @@ export default {
|
||||
try {
|
||||
const res = await fetchOeeSummary(this.buildQuery())
|
||||
// 兼容后端直接返回数组或 TableDataInfo { rows, total } 两种结构
|
||||
|
||||
|
||||
this.summaryList = res.data
|
||||
|
||||
|
||||
} catch (e) {
|
||||
this.$message.error('加载酸轧日汇总失败')
|
||||
} finally {
|
||||
@@ -589,7 +504,7 @@ export default {
|
||||
const res = await fetchOeeLoss7(this.buildQuery())
|
||||
// 兼容后端直接返回数组或 TableDataInfo { rows, total } 两种结构
|
||||
this.loss7List = res.data
|
||||
|
||||
|
||||
} catch (e) {
|
||||
this.$message.error('加载酸轧 7 大损失失败')
|
||||
} finally {
|
||||
@@ -749,10 +664,6 @@ export default {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.oee-top-warning {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.oee-main-row {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user