Merge branch '0.8.X' of http://49.232.154.205:10100/DeXun/klp-oa into 0.8.X
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
-- =====================================================================
|
||||
-- 轧辊管理多产线支持:为 4 张轧辊表添加 line_id 外键列
|
||||
-- 存量数据默认为 NULL,业务侧按需补填或忽略(NULL 表示未归属产线)
|
||||
-- =====================================================================
|
||||
|
||||
ALTER TABLE mes_roll_info
|
||||
ADD COLUMN line_id BIGINT NULL COMMENT '产线ID(关联 wms_production_line.line_id)' AFTER roll_id,
|
||||
ADD KEY idx_line_id (line_id);
|
||||
|
||||
ALTER TABLE mes_roll_change
|
||||
ADD COLUMN line_id BIGINT NULL COMMENT '产线ID(关联 wms_production_line.line_id)' AFTER change_id,
|
||||
ADD KEY idx_line_stand (line_id, stand_no);
|
||||
|
||||
ALTER TABLE mes_roll_standby
|
||||
ADD COLUMN line_id BIGINT NULL COMMENT '产线ID(关联 wms_production_line.line_id)' AFTER standby_id,
|
||||
ADD KEY idx_line_stand (line_id, stand_no);
|
||||
|
||||
ALTER TABLE mes_roll_grind
|
||||
ADD COLUMN line_id BIGINT NULL COMMENT '产线ID(关联 wms_production_line.line_id)' AFTER grind_id,
|
||||
ADD KEY idx_line_id (line_id);
|
||||
@@ -38,21 +38,21 @@ public class MesRollChangeController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定机架当前在机轧辊(最近一次换辊记录)
|
||||
* GET /mes/rollChange/current?standNo=1%23
|
||||
* 查询指定产线+机架当前在机轧辊(最近一次换辊记录)
|
||||
* GET /mes/rollChange/current?lineId=xxx&standNo=1%23
|
||||
*/
|
||||
@GetMapping("/current")
|
||||
public R<MesRollChangeVo> current(@RequestParam String standNo) {
|
||||
return R.ok(iMesRollChangeService.queryCurrentByStand(standNo));
|
||||
public R<MesRollChangeVo> current(Long lineId, @RequestParam String standNo) {
|
||||
return R.ok(iMesRollChangeService.queryCurrentByStand(lineId, standNo));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询各机架各辊位实时工作绩效(工作长度/卷数/重量)
|
||||
* GET /mes/rollChange/performance
|
||||
* 查询指定产线各机架各辊位实时工作绩效(工作长度/卷数/重量)
|
||||
* GET /mes/rollChange/performance?lineId=xxx
|
||||
*/
|
||||
@GetMapping("/performance")
|
||||
public R<Map<String, Map<String, Object>>> performance() {
|
||||
return R.ok(iMesRollChangeService.queryRollPerformance());
|
||||
public R<Map<String, Map<String, Object>>> performance(Long lineId) {
|
||||
return R.ok(iMesRollChangeService.queryRollPerformance(lineId));
|
||||
}
|
||||
|
||||
/** 详情 */
|
||||
|
||||
@@ -41,16 +41,16 @@ public class MesRollInfoController extends BaseController {
|
||||
return iMesRollInfoService.queryPageList(bo, pageQuery);
|
||||
}
|
||||
|
||||
/** 状态统计卡片数据 */
|
||||
/** 状态统计卡片数据(lineId 为空时统计全部产线) */
|
||||
@GetMapping("/stats")
|
||||
public R<Map<String, Object>> stats() {
|
||||
return R.ok(iMesRollInfoService.queryStatusStats());
|
||||
public R<Map<String, Object>> stats(Long lineId) {
|
||||
return R.ok(iMesRollInfoService.queryStatusStats(lineId));
|
||||
}
|
||||
|
||||
/** 轧辊编号下拉列表(按辊型 + 状态过滤,均可为空) */
|
||||
/** 轧辊编号下拉列表(lineId / rollType / status 均可为空) */
|
||||
@GetMapping("/options")
|
||||
public R<List<String>> options(String rollType, String status) {
|
||||
return R.ok(iMesRollInfoService.queryRollNoList(rollType, status));
|
||||
public R<List<String>> options(Long lineId, String rollType, String status) {
|
||||
return R.ok(iMesRollInfoService.queryRollNoList(lineId, rollType, status));
|
||||
}
|
||||
|
||||
/** 详情 */
|
||||
@@ -90,13 +90,13 @@ public class MesRollInfoController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从换辊记录同步在线状态
|
||||
* 从换辊记录同步在线状态(lineId 为空时同步全部产线)
|
||||
* 将当前各机架在机轧辊的状态强制修正为 Online,解决状态数据不一致问题
|
||||
*/
|
||||
@Log(title = "轧辊库-状态同步", businessType = BusinessType.UPDATE)
|
||||
@PostMapping("/syncStatus")
|
||||
public R<Void> syncStatus() {
|
||||
iMesRollInfoService.syncStatusFromChange();
|
||||
public R<Void> syncStatus(Long lineId) {
|
||||
iMesRollInfoService.syncStatusFromChange(lineId);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
@@ -28,12 +28,12 @@ public class MesRollStandbyController extends BaseController {
|
||||
private final IMesRollStandbyService iMesRollStandbyService;
|
||||
|
||||
/**
|
||||
* 查询指定机架的下批轧辊列表
|
||||
* GET /mes/rollStandby/list?standNo=1%23
|
||||
* 查询指定产线+机架的下批轧辊列表
|
||||
* GET /mes/rollStandby/list?lineId=xxx&standNo=1%23
|
||||
*/
|
||||
@GetMapping("/list")
|
||||
public R<List<MesRollStandbyVo>> list(@RequestParam String standNo) {
|
||||
return R.ok(iMesRollStandbyService.queryByStand(standNo));
|
||||
public R<List<MesRollStandbyVo>> list(Long lineId, @RequestParam String standNo) {
|
||||
return R.ok(iMesRollStandbyService.queryByStand(lineId, standNo));
|
||||
}
|
||||
|
||||
/** 详情 */
|
||||
@@ -66,12 +66,12 @@ public class MesRollStandbyController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空指定机架的全部下批轧辊
|
||||
* DELETE /mes/rollStandby/clear?standNo=1%23
|
||||
* 清空指定产线+机架的全部下批轧辊
|
||||
* DELETE /mes/rollStandby/clear?lineId=xxx&standNo=1%23
|
||||
*/
|
||||
@Log(title = "下批轧辊", businessType = BusinessType.DELETE)
|
||||
@DeleteMapping("/clear")
|
||||
public R<Void> clear(@RequestParam String standNo) {
|
||||
return toAjax(iMesRollStandbyService.clearByStand(standNo));
|
||||
public R<Void> clear(Long lineId, @RequestParam String standNo) {
|
||||
return toAjax(iMesRollStandbyService.clearByStand(lineId, standNo));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,7 @@ import java.util.Date;
|
||||
|
||||
/**
|
||||
* 换辊记录 mes_roll_change
|
||||
* 四辊轧机,双机架(1# / 2#)
|
||||
* 每次换辊记录 4 支辊:上工作辊、下工作辊、上支撑辊、下支撑辊
|
||||
* 四辊轧机,支持多产线,每次换辊记录 4 支辊:上工作辊、下工作辊、上支撑辊、下支撑辊
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@@ -25,6 +24,9 @@ public class MesRollChange extends BaseEntity {
|
||||
@TableId(value = "change_id")
|
||||
private Long changeId;
|
||||
|
||||
/** 产线ID */
|
||||
private Long lineId;
|
||||
|
||||
/** 换辊编号 */
|
||||
private String changeNo;
|
||||
|
||||
|
||||
@@ -46,6 +46,9 @@ public class MesRollGrind extends BaseEntity {
|
||||
@TableId(value = "grind_id")
|
||||
private Long grindId;
|
||||
|
||||
/** 产线ID */
|
||||
private Long lineId;
|
||||
|
||||
/** 轧辊ID */
|
||||
private Long rollId;
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ public class MesRollInfo extends BaseEntity {
|
||||
@TableId(value = "roll_id")
|
||||
private Long rollId;
|
||||
|
||||
/** 产线ID */
|
||||
private Long lineId;
|
||||
|
||||
/** 轧辊编号 */
|
||||
private String rollNo;
|
||||
|
||||
|
||||
@@ -30,6 +30,9 @@ public class MesRollStandby extends BaseEntity {
|
||||
@TableId(value = "standby_id")
|
||||
private Long standbyId;
|
||||
|
||||
/** 产线ID */
|
||||
private Long lineId;
|
||||
|
||||
/** 机架号:1# / 2# */
|
||||
private String standNo;
|
||||
|
||||
|
||||
@@ -18,6 +18,9 @@ public class MesRollChangeBo extends BaseEntity {
|
||||
|
||||
private Long changeId;
|
||||
|
||||
/** 产线ID(查询过滤 / 新增归属) */
|
||||
private Long lineId;
|
||||
|
||||
private String changeNo;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
@@ -18,6 +18,9 @@ public class MesRollGrindBo extends BaseEntity {
|
||||
|
||||
private Long grindId;
|
||||
|
||||
/** 产线ID(查询过滤用) */
|
||||
private Long lineId;
|
||||
|
||||
@NotNull(message = "轧辊ID不能为空")
|
||||
private Long rollId;
|
||||
|
||||
|
||||
@@ -22,6 +22,9 @@ public class MesRollInfoBo extends BaseEntity {
|
||||
@NotNull(message = "轧辊ID不能为空", groups = EditGroup.class)
|
||||
private Long rollId;
|
||||
|
||||
/** 产线ID(查询过滤 / 新增归属) */
|
||||
private Long lineId;
|
||||
|
||||
@NotBlank(message = "轧辊编号不能为空", groups = {AddGroup.class, EditGroup.class})
|
||||
private String rollNo;
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@ public class MesRollStandbyBo extends BaseEntity {
|
||||
|
||||
private Long standbyId;
|
||||
|
||||
/** 产线ID(查询过滤 / 新增归属) */
|
||||
private Long lineId;
|
||||
|
||||
/** 机架号:1# / 2# */
|
||||
@NotBlank(message = "机架号不能为空", groups = AddGroup.class)
|
||||
private String standNo;
|
||||
|
||||
@@ -17,6 +17,8 @@ public class MesRollChangeVo {
|
||||
|
||||
private Long changeId;
|
||||
|
||||
private Long lineId;
|
||||
|
||||
private String changeNo;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
@@ -19,6 +19,12 @@ public class MesRollInfoVo {
|
||||
@ExcelProperty("轧辊ID")
|
||||
private Long rollId;
|
||||
|
||||
@ExcelProperty("产线ID")
|
||||
private Long lineId;
|
||||
|
||||
@ExcelProperty("产线名称")
|
||||
private String lineName;
|
||||
|
||||
@ExcelProperty("轧辊编号")
|
||||
private String rollNo;
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ public class MesRollStandbyVo {
|
||||
|
||||
private Long standbyId;
|
||||
|
||||
private Long lineId;
|
||||
|
||||
/** 机架号:1# / 2# */
|
||||
private String standNo;
|
||||
|
||||
|
||||
@@ -13,27 +13,30 @@ import java.util.Map;
|
||||
*/
|
||||
public interface MesRollChangeMapper extends BaseMapperPlus<MesRollChangeMapper, MesRollChange, MesRollChangeVo> {
|
||||
|
||||
/** 查询指定机架最新一次换辊记录(当前在机轧辊) */
|
||||
MesRollChangeVo selectLatestByStand(@Param("standNo") String standNo);
|
||||
/** 查询指定产线+机架最新一次换辊记录(当前在机轧辊) */
|
||||
MesRollChangeVo selectLatestByStand(@Param("lineId") Long lineId, @Param("standNo") String standNo);
|
||||
|
||||
/**
|
||||
* 按辊位查询该位置最新一次有记录的换辊(支持部分换辊)
|
||||
* posType: upperWr / lowerWr / upperBr / lowerBr
|
||||
*/
|
||||
MesRollChangeVo selectLatestByStandAndPosition(@Param("standNo") String standNo,
|
||||
MesRollChangeVo selectLatestByStandAndPosition(@Param("lineId") Long lineId,
|
||||
@Param("standNo") String standNo,
|
||||
@Param("posType") String posType);
|
||||
|
||||
/**
|
||||
* 组合查询机架各辊位当前实际在机状态(每个位置独立取最新非空记录)
|
||||
* 组合查询指定产线+机架各辊位当前实际在机状态(每个位置独立取最新非空记录)
|
||||
* 返回 map,key 同 MesRollChangeVo 字段名(camelCase)
|
||||
*/
|
||||
Map<String, Object> selectCurrentStateByStand(@Param("standNo") String standNo);
|
||||
Map<String, Object> selectCurrentStateByStand(@Param("lineId") Long lineId, @Param("standNo") String standNo);
|
||||
|
||||
/**
|
||||
* 查询同机架下一次换辊时间(任意辊位有换辊即触发)
|
||||
* 查询同产线+机架下一次换辊时间(任意辊位有换辊即触发)
|
||||
* 返回 null 表示该辊仍在机
|
||||
*/
|
||||
Date selectNextChangeTime(@Param("standNo") String standNo, @Param("changeTime") Date changeTime);
|
||||
Date selectNextChangeTime(@Param("lineId") Long lineId,
|
||||
@Param("standNo") String standNo,
|
||||
@Param("changeTime") Date changeTime);
|
||||
|
||||
/**
|
||||
* 统计指定时间区间内的卷料生产统计:
|
||||
|
||||
@@ -15,13 +15,17 @@ public interface MesRollInfoMapper extends BaseMapperPlus<MesRollInfoMapper, Mes
|
||||
|
||||
/**
|
||||
* 按状态统计数量(用于总览卡片)
|
||||
* lineId 为 null 时统计全部产线
|
||||
*/
|
||||
List<Map<String, Object>> selectStatusStats();
|
||||
List<Map<String, Object>> selectStatusStats(@Param("lineId") Long lineId);
|
||||
|
||||
/**
|
||||
* 查询轧辊编号列表(用于下拉选择,可按辊型和状态过滤)
|
||||
* 查询轧辊编号列表(用于下拉选择,可按产线、辊型、状态过滤)
|
||||
* lineId 为 null 时不过滤产线
|
||||
*/
|
||||
List<String> selectRollNoList(@Param("rollType") String rollType, @Param("status") String status);
|
||||
List<String> selectRollNoList(@Param("lineId") Long lineId,
|
||||
@Param("rollType") String rollType,
|
||||
@Param("status") String status);
|
||||
|
||||
/**
|
||||
* 按轧辊编号查询(用于换辊时同步状态)
|
||||
|
||||
@@ -12,9 +12,9 @@ import java.util.List;
|
||||
*/
|
||||
public interface MesRollStandbyMapper extends BaseMapperPlus<MesRollStandbyMapper, MesRollStandby, MesRollStandbyVo> {
|
||||
|
||||
/** 按机架查询下批轧辊列表 */
|
||||
List<MesRollStandbyVo> selectByStand(@Param("standNo") String standNo);
|
||||
/** 按产线+机架查询下批轧辊列表 */
|
||||
List<MesRollStandbyVo> selectByStand(@Param("lineId") Long lineId, @Param("standNo") String standNo);
|
||||
|
||||
/** 清空某机架的下批轧辊 */
|
||||
int clearByStand(@Param("standNo") String standNo);
|
||||
/** 清空指定产线+机架的下批轧辊 */
|
||||
int clearByStand(@Param("lineId") Long lineId, @Param("standNo") String standNo);
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ public interface IMesRollChangeService {
|
||||
|
||||
MesRollChangeVo queryById(Long changeId);
|
||||
|
||||
/** 查询指定机架当前在机轧辊(最新一次换辊记录) */
|
||||
MesRollChangeVo queryCurrentByStand(String standNo);
|
||||
/** 查询指定产线+机架当前在机轧辊(最新一次换辊记录) */
|
||||
MesRollChangeVo queryCurrentByStand(Long lineId, String standNo);
|
||||
|
||||
TableDataInfo<MesRollChangeVo> queryPageList(MesRollChangeBo bo, PageQuery pageQuery);
|
||||
|
||||
@@ -28,9 +28,9 @@ public interface IMesRollChangeService {
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 查询各机架各辊位的实时工作绩效(工作长度/卷数/重量)
|
||||
* 查询指定产线各机架各辊位的实时工作绩效(工作长度/卷数/重量)
|
||||
* 返回结构: { posType -> { standNo -> { rollNo, workLength, coilCount, totalWeight } } }
|
||||
* posType: upperBr / upperWr / lowerWr / lowerBr
|
||||
*/
|
||||
Map<String, Map<String, Object>> queryRollPerformance();
|
||||
Map<String, Map<String, Object>> queryRollPerformance(Long lineId);
|
||||
}
|
||||
|
||||
@@ -20,11 +20,16 @@ public interface IMesRollInfoService {
|
||||
|
||||
List<MesRollInfoVo> queryList(MesRollInfoBo bo);
|
||||
|
||||
/** 按状态统计(总览卡片) */
|
||||
Map<String, Object> queryStatusStats();
|
||||
/**
|
||||
* 按状态统计(总览卡片)
|
||||
* lineId 为 null 时统计全部产线
|
||||
*/
|
||||
Map<String, Object> queryStatusStats(Long lineId);
|
||||
|
||||
/** 轧辊编号下拉列表(status 为 null 时不过滤状态) */
|
||||
List<String> queryRollNoList(String rollType, String status);
|
||||
/**
|
||||
* 轧辊编号下拉列表(lineId / rollType / status 均可为 null,不过滤对应维度)
|
||||
*/
|
||||
List<String> queryRollNoList(Long lineId, String rollType, String status);
|
||||
|
||||
Long insertByBo(MesRollInfoBo bo);
|
||||
|
||||
@@ -40,6 +45,7 @@ public interface IMesRollInfoService {
|
||||
* 每个机架最新一次换辊记录中的 4 支辊 → Online;
|
||||
* 下批辊列表中的辊 → Standby(已由 addStandby 维护,此处不重复处理);
|
||||
* 其余不变,仅修正当前在机辊的状态数据不一致问题。
|
||||
* lineId 为 null 时同步全部产线;指定 lineId 时仅同步该产线。
|
||||
*/
|
||||
void syncStatusFromChange();
|
||||
void syncStatusFromChange(Long lineId);
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ public interface IMesRollStandbyService {
|
||||
|
||||
MesRollStandbyVo queryById(Long standbyId);
|
||||
|
||||
/** 查询指定机架的下批轧辊列表 */
|
||||
List<MesRollStandbyVo> queryByStand(String standNo);
|
||||
/** 查询指定产线+机架的下批轧辊列表 */
|
||||
List<MesRollStandbyVo> queryByStand(Long lineId, String standNo);
|
||||
|
||||
/** 新增下批轧辊,同步将该辊状态更新为 Standby */
|
||||
Long addStandby(MesRollStandbyBo bo);
|
||||
@@ -24,6 +24,6 @@ public interface IMesRollStandbyService {
|
||||
/** 删除某条下批轧辊,同步将该辊状态恢复为 Offline */
|
||||
Boolean deleteById(Long standbyId);
|
||||
|
||||
/** 清空指定机架的全部下批轧辊,并将对应辊状态恢复为 Offline */
|
||||
Boolean clearByStand(String standNo);
|
||||
/** 清空指定产线+机架的全部下批轧辊,并将对应辊状态恢复为 Offline */
|
||||
Boolean clearByStand(Long lineId, String standNo);
|
||||
}
|
||||
|
||||
@@ -44,11 +44,12 @@ public class MesRollChangeServiceImpl implements IMesRollChangeService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MesRollChangeVo queryCurrentByStand(String standNo) {
|
||||
public MesRollChangeVo queryCurrentByStand(Long lineId, String standNo) {
|
||||
// 组合各辊位最新状态,转换为 VO
|
||||
Map<String, Object> state = baseMapper.selectCurrentStateByStand(standNo);
|
||||
Map<String, Object> state = baseMapper.selectCurrentStateByStand(lineId, standNo);
|
||||
if (state == null) return null;
|
||||
MesRollChangeVo vo = new MesRollChangeVo();
|
||||
vo.setLineId(lineId);
|
||||
vo.setStandNo(standNo);
|
||||
vo.setUpperWrNo(str(state.get("upperWrNo")));
|
||||
vo.setUpperWrDia(decimal(state.get("upperWrDia")));
|
||||
@@ -70,7 +71,7 @@ public class MesRollChangeServiceImpl implements IMesRollChangeService {
|
||||
Page<MesRollChangeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
// 为每条换辊记录计算:工作长度、卷数、重量
|
||||
for (MesRollChangeVo vo : result.getRecords()) {
|
||||
Date endTime = baseMapper.selectNextChangeTime(vo.getStandNo(), vo.getChangeTime());
|
||||
Date endTime = baseMapper.selectNextChangeTime(vo.getLineId(), vo.getStandNo(), vo.getChangeTime());
|
||||
Map<String, Object> stats = baseMapper.selectCoilStats(vo.getChangeTime(), endTime);
|
||||
fillStatsToVo(vo, stats);
|
||||
}
|
||||
@@ -81,12 +82,12 @@ public class MesRollChangeServiceImpl implements IMesRollChangeService {
|
||||
private static final List<String> STANDS = Arrays.asList("1#", "2#");
|
||||
|
||||
@Override
|
||||
public Map<String, Map<String, Object>> queryRollPerformance() {
|
||||
public Map<String, Map<String, Object>> queryRollPerformance(Long lineId) {
|
||||
Map<String, Map<String, Object>> result = new HashMap<>(4);
|
||||
for (String posType : POS_TYPES) {
|
||||
Map<String, Object> byStand = new HashMap<>(2);
|
||||
for (String standNo : STANDS) {
|
||||
MesRollChangeVo rec = baseMapper.selectLatestByStandAndPosition(standNo, posType);
|
||||
MesRollChangeVo rec = baseMapper.selectLatestByStandAndPosition(lineId, standNo, posType);
|
||||
Map<String, Object> cell = new HashMap<>(6);
|
||||
if (rec != null) {
|
||||
cell.put("rollNo", getRollNoByPos(rec, posType));
|
||||
@@ -106,9 +107,9 @@ public class MesRollChangeServiceImpl implements IMesRollChangeService {
|
||||
}
|
||||
|
||||
/** 若该辊位要换新辊,则把当前在机的旧辊下线 */
|
||||
private void offlineIfReplaced(String standNo, String posType, String newRollNo) {
|
||||
private void offlineIfReplaced(Long lineId, String standNo, String posType, String newRollNo) {
|
||||
if (StringUtils.isBlank(newRollNo)) return;
|
||||
MesRollChangeVo cur = baseMapper.selectLatestByStandAndPosition(standNo, posType);
|
||||
MesRollChangeVo cur = baseMapper.selectLatestByStandAndPosition(lineId, standNo, posType);
|
||||
if (cur != null) {
|
||||
String oldRollNo = getRollNoByPos(cur, posType);
|
||||
if (StringUtils.isNotBlank(oldRollNo)) {
|
||||
@@ -158,6 +159,7 @@ public class MesRollChangeServiceImpl implements IMesRollChangeService {
|
||||
|
||||
private LambdaQueryWrapper<MesRollChange> buildQueryWrapper(MesRollChangeBo bo) {
|
||||
LambdaQueryWrapper<MesRollChange> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getLineId() != null, MesRollChange::getLineId, bo.getLineId());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStandNo()), MesRollChange::getStandNo, bo.getStandNo());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getChangeType()), MesRollChange::getChangeType, bo.getChangeType());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getChangeStatus()), MesRollChange::getChangeStatus, bo.getChangeStatus());
|
||||
@@ -184,10 +186,10 @@ public class MesRollChangeServiceImpl implements IMesRollChangeService {
|
||||
add.setChangeType("三级换辊");
|
||||
}
|
||||
// 按辊位独立换辊:只下线被替换的那个位置的旧辊
|
||||
offlineIfReplaced(add.getStandNo(), "upperWr", add.getUpperWrNo());
|
||||
offlineIfReplaced(add.getStandNo(), "lowerWr", add.getLowerWrNo());
|
||||
offlineIfReplaced(add.getStandNo(), "upperBr", add.getUpperBrNo());
|
||||
offlineIfReplaced(add.getStandNo(), "lowerBr", add.getLowerBrNo());
|
||||
offlineIfReplaced(add.getLineId(), add.getStandNo(), "upperWr", add.getUpperWrNo());
|
||||
offlineIfReplaced(add.getLineId(), add.getStandNo(), "lowerWr", add.getLowerWrNo());
|
||||
offlineIfReplaced(add.getLineId(), add.getStandNo(), "upperBr", add.getUpperBrNo());
|
||||
offlineIfReplaced(add.getLineId(), add.getStandNo(), "lowerBr", add.getLowerBrNo());
|
||||
|
||||
baseMapper.insert(add);
|
||||
|
||||
|
||||
@@ -52,8 +52,8 @@ public class MesRollInfoServiceImpl implements IMesRollInfoService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> queryStatusStats() {
|
||||
List<Map<String, Object>> rows = baseMapper.selectStatusStats();
|
||||
public Map<String, Object> queryStatusStats(Long lineId) {
|
||||
List<Map<String, Object>> rows = baseMapper.selectStatusStats(lineId);
|
||||
Map<String, Object> result = new HashMap<String, Object>(8);
|
||||
int total = 0;
|
||||
for (Map<String, Object> row : rows) {
|
||||
@@ -67,12 +67,13 @@ public class MesRollInfoServiceImpl implements IMesRollInfoService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> queryRollNoList(String rollType, String status) {
|
||||
return baseMapper.selectRollNoList(rollType, status);
|
||||
public List<String> queryRollNoList(Long lineId, String rollType, String status) {
|
||||
return baseMapper.selectRollNoList(lineId, rollType, status);
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<MesRollInfo> buildQueryWrapper(MesRollInfoBo bo) {
|
||||
LambdaQueryWrapper<MesRollInfo> lqw = Wrappers.lambdaQuery();
|
||||
lqw.eq(bo.getLineId() != null, MesRollInfo::getLineId, bo.getLineId());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getRollNo()), MesRollInfo::getRollNo, bo.getRollNo());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getRollType()), MesRollInfo::getRollType, bo.getRollType());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), MesRollInfo::getStatus, bo.getStatus());
|
||||
@@ -120,11 +121,11 @@ public class MesRollInfoServiceImpl implements IMesRollInfoService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void syncStatusFromChange() {
|
||||
public void syncStatusFromChange(Long lineId) {
|
||||
// 按辊位独立取最新在机辊,确保各位置状态正确
|
||||
for (String standNo : Arrays.asList("1#", "2#")) {
|
||||
for (String posType : Arrays.asList("upperWr", "lowerWr", "upperBr", "lowerBr")) {
|
||||
MesRollChangeVo rec = rollChangeMapper.selectLatestByStandAndPosition(standNo, posType);
|
||||
MesRollChangeVo rec = rollChangeMapper.selectLatestByStandAndPosition(lineId, standNo, posType);
|
||||
if (rec == null) continue;
|
||||
String rollNo = getRollNoByPos(rec, posType);
|
||||
if (StringUtils.isNotBlank(rollNo)) {
|
||||
|
||||
@@ -31,8 +31,8 @@ public class MesRollStandbyServiceImpl implements IMesRollStandbyService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MesRollStandbyVo> queryByStand(String standNo) {
|
||||
return baseMapper.selectByStand(standNo);
|
||||
public List<MesRollStandbyVo> queryByStand(Long lineId, String standNo) {
|
||||
return baseMapper.selectByStand(lineId, standNo);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,10 +70,10 @@ public class MesRollStandbyServiceImpl implements IMesRollStandbyService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Boolean clearByStand(String standNo) {
|
||||
public Boolean clearByStand(Long lineId, String standNo) {
|
||||
// 先查出所有辊号再清空
|
||||
List<MesRollStandbyVo> list = baseMapper.selectByStand(standNo);
|
||||
int rows = baseMapper.clearByStand(standNo);
|
||||
List<MesRollStandbyVo> list = baseMapper.selectByStand(lineId, standNo);
|
||||
int rows = baseMapper.clearByStand(lineId, standNo);
|
||||
for (MesRollStandbyVo vo : list) {
|
||||
if (StringUtils.isNotBlank(vo.getRollNo())) {
|
||||
// 只有仍处于 Standby 状态时才回退为 Offline(换辊后已变 Online 的不动)
|
||||
|
||||
@@ -2,26 +2,28 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.klp.mes.roll.mapper.MesRollChangeMapper">
|
||||
|
||||
<!-- 查询指定机架最新一次换辊记录(即当前在机轧辊) -->
|
||||
<!-- 查询指定产线+机架最新一次换辊记录(即当前在机轧辊) -->
|
||||
<select id="selectLatestByStand" resultType="com.klp.mes.roll.domain.vo.MesRollChangeVo">
|
||||
SELECT change_id, change_no, change_time, stand_no, change_type, change_status, operator,
|
||||
SELECT change_id, line_id, change_no, change_time, stand_no, change_type, change_status, operator,
|
||||
upper_wr_no, upper_wr_dia, lower_wr_no, lower_wr_dia,
|
||||
upper_br_no, upper_br_dia, lower_br_no, lower_br_dia,
|
||||
remark, create_time
|
||||
FROM mes_roll_change
|
||||
WHERE del_flag = 0
|
||||
AND stand_no = #{standNo}
|
||||
<if test="lineId != null">AND line_id = #{lineId}</if>
|
||||
ORDER BY change_time DESC, change_id DESC
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- 按辊位查最新非空换辊记录(支持部分换辊) -->
|
||||
<select id="selectLatestByStandAndPosition" resultType="com.klp.mes.roll.domain.vo.MesRollChangeVo">
|
||||
SELECT change_id, change_no, change_time, stand_no, operator,
|
||||
SELECT change_id, line_id, change_no, change_time, stand_no, operator,
|
||||
upper_wr_no, upper_wr_dia, lower_wr_no, lower_wr_dia,
|
||||
upper_br_no, upper_br_dia, lower_br_no, lower_br_dia
|
||||
FROM mes_roll_change
|
||||
WHERE del_flag = 0 AND stand_no = #{standNo}
|
||||
<if test="lineId != null">AND line_id = #{lineId}</if>
|
||||
<choose>
|
||||
<when test="posType == 'upperWr'">AND upper_wr_no IS NOT NULL AND upper_wr_no != ''</when>
|
||||
<when test="posType == 'lowerWr'">AND lower_wr_no IS NOT NULL AND lower_wr_no != ''</when>
|
||||
@@ -35,24 +37,25 @@
|
||||
<!-- 组合查询各辊位当前实际在机状态(每个位置独立取最新非空记录) -->
|
||||
<select id="selectCurrentStateByStand" resultType="map">
|
||||
SELECT
|
||||
(SELECT upper_wr_no FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 AND upper_wr_no IS NOT NULL AND upper_wr_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS upperWrNo,
|
||||
(SELECT upper_wr_dia FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 AND upper_wr_no IS NOT NULL AND upper_wr_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS upperWrDia,
|
||||
(SELECT lower_wr_no FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 AND lower_wr_no IS NOT NULL AND lower_wr_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS lowerWrNo,
|
||||
(SELECT lower_wr_dia FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 AND lower_wr_no IS NOT NULL AND lower_wr_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS lowerWrDia,
|
||||
(SELECT upper_br_no FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 AND upper_br_no IS NOT NULL AND upper_br_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS upperBrNo,
|
||||
(SELECT upper_br_dia FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 AND upper_br_no IS NOT NULL AND upper_br_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS upperBrDia,
|
||||
(SELECT lower_br_no FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 AND lower_br_no IS NOT NULL AND lower_br_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS lowerBrNo,
|
||||
(SELECT lower_br_dia FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 AND lower_br_no IS NOT NULL AND lower_br_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS lowerBrDia,
|
||||
(SELECT MAX(change_time) FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0) AS changeTime
|
||||
(SELECT upper_wr_no FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 <if test="lineId != null">AND line_id = #{lineId}</if> AND upper_wr_no IS NOT NULL AND upper_wr_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS upperWrNo,
|
||||
(SELECT upper_wr_dia FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 <if test="lineId != null">AND line_id = #{lineId}</if> AND upper_wr_no IS NOT NULL AND upper_wr_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS upperWrDia,
|
||||
(SELECT lower_wr_no FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 <if test="lineId != null">AND line_id = #{lineId}</if> AND lower_wr_no IS NOT NULL AND lower_wr_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS lowerWrNo,
|
||||
(SELECT lower_wr_dia FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 <if test="lineId != null">AND line_id = #{lineId}</if> AND lower_wr_no IS NOT NULL AND lower_wr_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS lowerWrDia,
|
||||
(SELECT upper_br_no FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 <if test="lineId != null">AND line_id = #{lineId}</if> AND upper_br_no IS NOT NULL AND upper_br_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS upperBrNo,
|
||||
(SELECT upper_br_dia FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 <if test="lineId != null">AND line_id = #{lineId}</if> AND upper_br_no IS NOT NULL AND upper_br_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS upperBrDia,
|
||||
(SELECT lower_br_no FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 <if test="lineId != null">AND line_id = #{lineId}</if> AND lower_br_no IS NOT NULL AND lower_br_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS lowerBrNo,
|
||||
(SELECT lower_br_dia FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 <if test="lineId != null">AND line_id = #{lineId}</if> AND lower_br_no IS NOT NULL AND lower_br_no != '' ORDER BY change_time DESC, change_id DESC LIMIT 1) AS lowerBrDia,
|
||||
(SELECT MAX(change_time) FROM mes_roll_change WHERE stand_no = #{standNo} AND del_flag = 0 <if test="lineId != null">AND line_id = #{lineId}</if>) AS changeTime
|
||||
</select>
|
||||
|
||||
<!-- 查询同机架在本次换辊之后最近一次换辊的时间 -->
|
||||
<!-- 查询同产线+机架在本次换辊之后最近一次换辊的时间 -->
|
||||
<select id="selectNextChangeTime" resultType="java.util.Date">
|
||||
SELECT MIN(change_time)
|
||||
FROM mes_roll_change
|
||||
WHERE del_flag = 0
|
||||
AND stand_no = #{standNo}
|
||||
AND change_time > #{changeTime}
|
||||
<if test="lineId != null">AND line_id = #{lineId}</if>
|
||||
</select>
|
||||
|
||||
<!--
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
SELECT status, COUNT(*) AS cnt
|
||||
FROM mes_roll_info
|
||||
WHERE del_flag = 0
|
||||
<if test="lineId != null">AND line_id = #{lineId}</if>
|
||||
GROUP BY status
|
||||
</select>
|
||||
|
||||
@@ -15,6 +16,7 @@
|
||||
SELECT roll_no FROM mes_roll_info
|
||||
WHERE del_flag = 0
|
||||
AND status != 'Scrapped'
|
||||
<if test="lineId != null">AND line_id = #{lineId}</if>
|
||||
<if test="rollType != null and rollType != ''">
|
||||
AND roll_type = #{rollType}
|
||||
</if>
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.klp.mes.roll.mapper.MesRollStandbyMapper">
|
||||
|
||||
<!-- 按机架查询下批轧辊,附带辊位中文标签 -->
|
||||
<!-- 按产线+机架查询下批轧辊,附带辊位中文标签 -->
|
||||
<select id="selectByStand" resultType="com.klp.mes.roll.domain.vo.MesRollStandbyVo">
|
||||
SELECT
|
||||
standby_id, stand_no, roll_no, roll_type, position,
|
||||
standby_id, line_id, stand_no, roll_no, roll_type, position,
|
||||
CASE
|
||||
WHEN roll_type = 'BR' AND position = 'UP' THEN '上支撑辊'
|
||||
WHEN roll_type = 'WR' AND position = 'UP' THEN '上工作辊'
|
||||
@@ -17,17 +17,19 @@
|
||||
FROM mes_roll_standby
|
||||
WHERE del_flag = 0
|
||||
AND stand_no = #{standNo}
|
||||
<if test="lineId != null">AND line_id = #{lineId}</if>
|
||||
ORDER BY
|
||||
FIELD(roll_type, 'BR', 'WR', 'WR', 'BR'),
|
||||
FIELD(position, 'UP', 'UP', 'DOWN', 'DOWN')
|
||||
</select>
|
||||
|
||||
<!-- 逻辑删除指定机架所有下批轧辊(清空) -->
|
||||
<!-- 逻辑删除指定产线+机架所有下批轧辊(清空) -->
|
||||
<update id="clearByStand">
|
||||
UPDATE mes_roll_standby
|
||||
SET del_flag = 1
|
||||
WHERE del_flag = 0
|
||||
AND stand_no = #{standNo}
|
||||
<if test="lineId != null">AND line_id = #{lineId}</if>
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询换辊记录分页列表(支持按机架、类型、时间筛选)
|
||||
// 查询换辊记录分页列表(支持按产线、机架、类型、时间筛选)
|
||||
export function listRollChange(query) {
|
||||
return request({
|
||||
url: '/mes/rollChange/list',
|
||||
@@ -9,20 +9,21 @@ export function listRollChange(query) {
|
||||
})
|
||||
}
|
||||
|
||||
// 查询指定机架当前在机轧辊(最近一次换辊记录)
|
||||
export function getCurrentRolls(standNo) {
|
||||
// 查询指定产线+机架当前在机轧辊(最近一次换辊记录)
|
||||
export function getCurrentRolls(lineId, standNo) {
|
||||
return request({
|
||||
url: '/mes/rollChange/current',
|
||||
method: 'get',
|
||||
params: { standNo }
|
||||
params: { lineId, standNo }
|
||||
})
|
||||
}
|
||||
|
||||
// 查询各机架各辊位实时工作绩效(workLength/coilCount/totalWeight)
|
||||
export function getRollPerformance() {
|
||||
// 查询指定产线各机架各辊位实时工作绩效(workLength/coilCount/totalWeight)
|
||||
export function getRollPerformance(lineId) {
|
||||
return request({
|
||||
url: '/mes/rollChange/performance',
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
params: { lineId }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -9,20 +9,21 @@ export function listRollInfo(query) {
|
||||
})
|
||||
}
|
||||
|
||||
// 查询状态统计卡片
|
||||
export function getRollStats() {
|
||||
// 查询状态统计卡片(lineId 为空时统计全部产线)
|
||||
export function getRollStats(lineId) {
|
||||
return request({
|
||||
url: '/mes/rollInfo/stats',
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
params: { lineId }
|
||||
})
|
||||
}
|
||||
|
||||
// 查询轧辊编号下拉(rollType/status 均可为空,不传则不过滤)
|
||||
export function listRollOptions(rollType, status) {
|
||||
// 查询轧辊编号下拉(lineId/rollType/status 均可为空,不传则不过滤)
|
||||
export function listRollOptions(lineId, rollType, status) {
|
||||
return request({
|
||||
url: '/mes/rollInfo/options',
|
||||
method: 'get',
|
||||
params: { rollType, status }
|
||||
params: { lineId, rollType, status }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -68,11 +69,12 @@ export function scrapRollInfo(rollId) {
|
||||
})
|
||||
}
|
||||
|
||||
// 从换辊记录同步在线状态
|
||||
export function syncRollStatus() {
|
||||
// 从换辊记录同步在线状态(lineId 为空时同步全部产线)
|
||||
export function syncRollStatus(lineId) {
|
||||
return request({
|
||||
url: '/mes/rollInfo/syncStatus',
|
||||
method: 'post'
|
||||
method: 'post',
|
||||
params: { lineId }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询指定机架下批轧辊列表
|
||||
export function listRollStandby(standNo) {
|
||||
// 查询指定产线+机架下批轧辊列表
|
||||
export function listRollStandby(lineId, standNo) {
|
||||
return request({
|
||||
url: '/mes/rollStandby/list',
|
||||
method: 'get',
|
||||
params: { standNo }
|
||||
params: { lineId, standNo }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -43,11 +43,11 @@ export function delRollStandby(standbyId) {
|
||||
})
|
||||
}
|
||||
|
||||
// 清空指定机架全部下批轧辊
|
||||
export function clearRollStandby(standNo) {
|
||||
// 清空指定产线+机架全部下批轧辊
|
||||
export function clearRollStandby(lineId, standNo) {
|
||||
return request({
|
||||
url: '/mes/rollStandby/clear',
|
||||
method: 'delete',
|
||||
params: { standNo }
|
||||
params: { lineId, standNo }
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,7 +2,21 @@
|
||||
<div class="app-container grind-page">
|
||||
<div class="grind-layout">
|
||||
|
||||
<!-- ── 左侧:轧辊选择 ── -->
|
||||
<!-- ── 产线 Tab 列 ── -->
|
||||
<div class="line-tabs">
|
||||
<div
|
||||
:class="['lt-item', filterLineId === null ? 'lt-item--active' : '']"
|
||||
@click="handleLineTab(null)"
|
||||
>全部</div>
|
||||
<div
|
||||
v-for="l in sortedProductionLines"
|
||||
:key="l.lineId"
|
||||
:class="['lt-item', filterLineId === l.lineId ? 'lt-item--active' : '']"
|
||||
@click="handleLineTab(l.lineId)"
|
||||
>{{ l.lineName }}</div>
|
||||
</div>
|
||||
|
||||
<!-- ── 轧辊列表 ── -->
|
||||
<div class="grind-left">
|
||||
<el-card shadow="never" class="grind-card h-full">
|
||||
<div slot="header" class="card-header">
|
||||
@@ -11,12 +25,6 @@
|
||||
|
||||
<!-- 搜索过滤 -->
|
||||
<div class="roll-filter">
|
||||
<!-- 产线筛选(暂时写死双机架) -->
|
||||
<el-select v-model="filterLine" size="small" placeholder="选择产线"
|
||||
style="width:100%;margin-bottom:8px" @change="filterRolls">
|
||||
<el-option label="全部产线" value="" />
|
||||
<el-option label="双机架产线" value="双机架" />
|
||||
</el-select>
|
||||
<el-input v-model="filterNo" size="small" placeholder="编号搜索" prefix-icon="el-icon-search"
|
||||
clearable @input="filterRolls" style="margin-bottom:8px" />
|
||||
<el-radio-group v-model="filterType" size="small" @change="filterRolls" style="margin-bottom:8px">
|
||||
@@ -39,6 +47,7 @@
|
||||
<span :class="['ri-status', 'st-' + r.status]">{{ statusLabel(r.status) }}</span>
|
||||
<span class="ri-dia">φ{{ r.currentDia != null ? r.currentDia : r.initialDia }}</span>
|
||||
</div>
|
||||
<div v-if="filterLineId === null && r.lineName" class="ri-line">{{ r.lineName }}</div>
|
||||
</div>
|
||||
<div v-if="!rollLoading && filteredRolls.length === 0" class="roll-empty">暂无数据</div>
|
||||
</div>
|
||||
@@ -229,13 +238,18 @@
|
||||
<script>
|
||||
import { listRollInfo, getRollInfo } from '@/api/mes/roll/rollInfo'
|
||||
import { listRollGrind, addRollGrind, updateRollGrind, delRollGrind, getMonthlyStats } from '@/api/mes/roll/rollGrind'
|
||||
import { listProductionLine } from '@/api/wms/productionLine'
|
||||
import rollLineMixin from '../rollLineMixin'
|
||||
|
||||
export default {
|
||||
name: 'GrindRoom',
|
||||
mixins: [rollLineMixin],
|
||||
data() {
|
||||
return {
|
||||
// 产线定义(暂写死,后续接后端)
|
||||
LINES: [{ label: '双机架产线', value: '双机架' }],
|
||||
// 产线列表
|
||||
productionLines: [],
|
||||
filterLineId: null,
|
||||
lineTabOrder: [], // 本地记录的点击顺序(最近点过的在前)
|
||||
|
||||
// 左侧辊列表
|
||||
rollLoading: false,
|
||||
@@ -243,7 +257,6 @@ export default {
|
||||
filteredRolls: [],
|
||||
filterNo: '',
|
||||
filterType: '',
|
||||
filterLine: '',
|
||||
|
||||
// 右侧选中辊
|
||||
selectedRollId: null,
|
||||
@@ -270,6 +283,19 @@ export default {
|
||||
return this.$store.state.user.name || this.$store.getters.name || ''
|
||||
},
|
||||
|
||||
// 产线 tab 按本地点击顺序排序
|
||||
sortedProductionLines() {
|
||||
const order = this.lineTabOrder
|
||||
return [...this.productionLines].sort((a, b) => {
|
||||
const ai = order.indexOf(a.lineId)
|
||||
const bi = order.indexOf(b.lineId)
|
||||
if (ai === -1 && bi === -1) return 0
|
||||
if (ai === -1) return 1
|
||||
if (bi === -1) return -1
|
||||
return ai - bi
|
||||
})
|
||||
},
|
||||
|
||||
// 表格数据:新增时在顶部插入一个编辑行占位
|
||||
tableData() {
|
||||
if (this.editRow && this.editRow.__isNew) {
|
||||
@@ -279,26 +305,46 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.loadRolls()
|
||||
},
|
||||
created() {},
|
||||
|
||||
methods: {
|
||||
onLineResolved() {
|
||||
this.filterLineId = this.lineId
|
||||
const uid = this.$store.state.user?.userId || 0
|
||||
try { this.lineTabOrder = JSON.parse(localStorage.getItem(`grind_line_order_${uid}`) || '[]') } catch {}
|
||||
listProductionLine({ pageNum: 1, pageSize: 100 }).then(res => {
|
||||
this.productionLines = res.rows || []
|
||||
})
|
||||
this.loadRolls()
|
||||
},
|
||||
|
||||
// ── 轧辊列表 ──────────────────────────────────────
|
||||
loadRolls() {
|
||||
this.rollLoading = true
|
||||
listRollInfo({ pageNum: 1, pageSize: 500 }).then(res => {
|
||||
listRollInfo({ pageNum: 1, pageSize: 500, lineId: this.filterLineId }).then(res => {
|
||||
this.allRolls = res.rows || []
|
||||
this.filterRolls()
|
||||
}).finally(() => { this.rollLoading = false })
|
||||
},
|
||||
handleLineTab(lineId) {
|
||||
if (this.filterLineId === lineId) return
|
||||
this.filterLineId = lineId
|
||||
if (lineId !== null) {
|
||||
this.lineTabOrder = [lineId, ...this.lineTabOrder.filter(id => id !== lineId)]
|
||||
const uid = this.$store.state.user?.userId || 0
|
||||
localStorage.setItem(`grind_line_order_${uid}`, JSON.stringify(this.lineTabOrder))
|
||||
}
|
||||
if (this.editRow) this.cancelEdit()
|
||||
this.selectedRollId = null
|
||||
this.selectedRoll = null
|
||||
this.grindList = []
|
||||
this.loadRolls()
|
||||
},
|
||||
filterRolls() {
|
||||
this.filteredRolls = this.allRolls.filter(r => {
|
||||
const matchNo = !this.filterNo || r.rollNo.includes(this.filterNo)
|
||||
const matchType = !this.filterType || r.rollType === this.filterType
|
||||
// 后续 mes_roll_info 增加 line_code 字段后改为 r.lineCode === this.filterLine
|
||||
const matchLine = !this.filterLine || this.filterLine === '双机架'
|
||||
return matchNo && matchType && matchLine
|
||||
return matchNo && matchType
|
||||
})
|
||||
},
|
||||
selectRoll(r) {
|
||||
@@ -428,8 +474,38 @@ export default {
|
||||
.grind-page { background: #f4f5f7; height: 100%; }
|
||||
.grind-layout { display: flex; gap: 12px; height: 100%; }
|
||||
|
||||
/* 左侧 */
|
||||
.grind-left { width: 240px; flex-shrink: 0; }
|
||||
/* 产线 Tab 列 */
|
||||
.line-tabs {
|
||||
width: 64px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
.lt-item {
|
||||
padding: 10px 4px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #5f6368;
|
||||
background: #fff;
|
||||
border: 1px solid #dcdee0;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
transition: all .15s;
|
||||
}
|
||||
.lt-item:hover { background: #f0f6ff; color: #409eff; border-color: #c6d9f5; }
|
||||
.lt-item--active {
|
||||
background: #409eff;
|
||||
color: #fff;
|
||||
border-color: #409eff;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* 轧辊列表 */
|
||||
.grind-left { width: 220px; flex-shrink: 0; }
|
||||
.grind-right { flex: 1; min-width: 0; }
|
||||
|
||||
.grind-card { border: 1px solid #dcdee0; border-radius: 4px; }
|
||||
@@ -451,6 +527,7 @@ export default {
|
||||
.ri-no { font-family: 'Consolas', monospace; font-size: 13px; font-weight: 600; color: #1f2329; }
|
||||
.ri-meta { display: flex; align-items: center; gap: 6px; margin-top: 3px; }
|
||||
.ri-dia { font-size: 11px; color: #9aa0a6; }
|
||||
.ri-line { font-size: 10px; color: #b0b3bb; margin-top: 2px; }
|
||||
.ri-status { font-size: 11px; }
|
||||
.roll-empty { text-align: center; color: #c0c4cc; padding: 20px 0; font-size: 12px; }
|
||||
|
||||
|
||||
@@ -46,14 +46,8 @@
|
||||
|
||||
<!-- 搜索栏 -->
|
||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
||||
<el-form-item label="所属产线">
|
||||
<!-- 暂时写死双机架,后续接后端 line_code 字段 -->
|
||||
<el-select v-model="queryParams.lineCode" placeholder="全部产线" clearable style="width:130px" @change="handleQuery">
|
||||
<el-option label="双机架产线" value="双机架" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="轧辊编号" prop="rollNo">
|
||||
<el-input v-model="queryParams.rollNo" placeholder="请输入轧辊编号" clearable @keyup.enter.native="handleQuery" />
|
||||
<el-input v-model="queryParams.rollNo" placeholder="请输入轧辊编号" clearable @keyup.enter.native="handleQuery" />
|
||||
</el-form-item>
|
||||
<el-form-item label="辊型" prop="rollType">
|
||||
<el-select v-model="queryParams.rollType" placeholder="全部" clearable style="width:120px">
|
||||
@@ -225,9 +219,11 @@
|
||||
|
||||
<script>
|
||||
import { listRollInfo, getRollStats, getRollInfo, addRollInfo, updateRollInfo, delRollInfo, scrapRollInfo, syncRollStatus } from '@/api/mes/roll/rollInfo'
|
||||
import rollLineMixin from '../rollLineMixin'
|
||||
|
||||
export default {
|
||||
name: 'RollOverview',
|
||||
mixins: [rollLineMixin],
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
@@ -242,7 +238,7 @@ export default {
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
lineCode: undefined, // 产线筛选(暂写死双机架,后续接字段)
|
||||
lineId: undefined,
|
||||
rollNo: undefined,
|
||||
rollType: undefined,
|
||||
status: undefined
|
||||
@@ -255,11 +251,12 @@ export default {
|
||||
ids: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getStats()
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
onLineResolved() {
|
||||
this.queryParams.lineId = this.lineId
|
||||
this.getStats()
|
||||
this.getList()
|
||||
},
|
||||
getList() {
|
||||
this.loading = true
|
||||
listRollInfo(this.queryParams).then(res => {
|
||||
@@ -269,14 +266,14 @@ export default {
|
||||
}).catch(() => { this.loading = false })
|
||||
},
|
||||
getStats() {
|
||||
getRollStats().then(res => { this.stats = res.data || {} })
|
||||
getRollStats(this.lineId).then(res => { this.stats = res.data || {} })
|
||||
},
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
resetQuery() {
|
||||
this.queryParams.lineCode = undefined
|
||||
this.queryParams.lineId = this.lineId
|
||||
this.resetForm('queryForm')
|
||||
this.handleQuery()
|
||||
},
|
||||
@@ -287,6 +284,7 @@ export default {
|
||||
},
|
||||
handleAdd() {
|
||||
this.reset()
|
||||
this.form.lineId = this.lineId
|
||||
this.title = '新增轧辊'
|
||||
this.open = true
|
||||
},
|
||||
@@ -320,7 +318,7 @@ export default {
|
||||
},
|
||||
handleSyncStatus() {
|
||||
this.$modal.confirm('将从换辊记录中重新推算各机架当前在机轧辊的状态,确认执行?').then(() => {
|
||||
return syncRollStatus()
|
||||
return syncRollStatus(this.lineId)
|
||||
}).then(() => {
|
||||
this.getList()
|
||||
this.getStats()
|
||||
|
||||
22
klp-ui/src/views/mes/roll/rollLineMixin.js
Normal file
22
klp-ui/src/views/mes/roll/rollLineMixin.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 从 URL query.lineId 读取产线 ID,供各轧辊页面共用。
|
||||
* 组件覆盖 onLineResolved() 执行初始化加载。
|
||||
*
|
||||
* 菜单路径示例:/mes/roll/overview?lineId=1
|
||||
*/
|
||||
export default {
|
||||
computed: {
|
||||
lineId() {
|
||||
const v = this.$route.query.lineId
|
||||
return v != null ? Number(v) : null
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.onLineResolved()
|
||||
},
|
||||
|
||||
methods: {
|
||||
onLineResolved() {}
|
||||
}
|
||||
}
|
||||
707
klp-ui/src/views/mes/roll/working-single/index.vue
Normal file
707
klp-ui/src/views/mes/roll/working-single/index.vue
Normal file
@@ -0,0 +1,707 @@
|
||||
<template>
|
||||
<div class="app-container working-roll-page">
|
||||
|
||||
<!-- ① 顶部行:作业辊一览(2/3)+ 可用轧辊(1/3) -->
|
||||
<div class="top-row">
|
||||
|
||||
<!-- 作业辊一览 -->
|
||||
<el-card shadow="never" class="roll-table-card top-row__main">
|
||||
<div slot="header" class="card-header">
|
||||
<span class="card-title"><i class="el-icon-s-grid" /> 作业辊一览</span>
|
||||
<span class="header-meta" v-if="current.changeTime">末次换辊:{{ current.changeTime }}</span>
|
||||
<span style="margin-left:auto;display:flex;align-items:center;gap:6px">
|
||||
<el-button size="mini" icon="el-icon-refresh-right" @click="handleOpenChange">换辊</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button type="success" size="mini" icon="el-icon-plus" @click="handleAddStandby">添加下批</el-button>
|
||||
<el-button type="danger" size="mini" icon="el-icon-delete" @click="handleClearStandby"
|
||||
:disabled="!standbyList.length">清空下批</el-button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
ref="mainTable"
|
||||
:data="mergedRows"
|
||||
v-loading="loadingCurrent || loadingStandby"
|
||||
:row-class-name="rowClass"
|
||||
:span-method="mergeParamCol"
|
||||
border
|
||||
size="small"
|
||||
style="width:100%"
|
||||
>
|
||||
<el-table-column label="参数" width="156" align="left">
|
||||
<template slot-scope="scope">
|
||||
<span :class="['param-label', scope.row.group]">{{ scope.row.label }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="当前在机轧辊" align="center" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
<param-cell :data="scope.row.cur" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="下批轧辊" align="center" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
<div
|
||||
:class="['sb-cell',
|
||||
scope.row.sb && scope.row.sb.standbyId ? 'sb-cell--del' :
|
||||
scope.row.rollType ? 'sb-cell--add' : '']"
|
||||
@click="handleSbCellClick(scope.row.sb, scope.row.rollType, scope.row.position)"
|
||||
>
|
||||
<param-cell :data="scope.row.sb" />
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- 右侧:可用辊 + 工作绩效 -->
|
||||
<div class="top-row__aside aside-col">
|
||||
|
||||
<!-- 可用轧辊(离线) -->
|
||||
<el-card shadow="never" class="roll-table-card aside-panel">
|
||||
<div slot="header" class="card-header">
|
||||
<span class="card-title"><i class="el-icon-files" /> 可用轧辊(离线)</span>
|
||||
<el-button size="mini" icon="el-icon-refresh" style="margin-left:auto" @click="loadOfflineRolls">刷新</el-button>
|
||||
</div>
|
||||
<el-table v-loading="offlineLoading" :data="offlineRolls" size="small" :height="asideHalfH" style="width:100%">
|
||||
<el-table-column label="辊型" align="center" prop="rollType" width="52">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="mini" :type="scope.row.rollType === 'WR' ? 'primary' : 'warning'">{{ scope.row.rollType }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="轧辊编号" align="center" prop="rollNo" min-width="90" show-overflow-tooltip />
|
||||
<el-table-column label="辊径(mm)" align="center" width="76">
|
||||
<template slot-scope="scope">{{ scope.row.currentDia != null ? scope.row.currentDia : scope.row.initialDia }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="粗糙度" align="center" prop="roughness" width="64" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- 工作绩效 -->
|
||||
<el-card shadow="never" class="roll-table-card aside-panel">
|
||||
<div slot="header" class="card-header">
|
||||
<span class="card-title"><i class="el-icon-data-analysis" /> 工作绩效(实时)</span>
|
||||
<el-button size="mini" icon="el-icon-refresh" style="margin-left:auto" @click="loadRollPerformance">刷新</el-button>
|
||||
</div>
|
||||
<el-table v-loading="perfLoading" :data="perfRows" size="small" :height="asideHalfH" border style="width:100%">
|
||||
<el-table-column label="辊位" align="center" prop="label" width="80" />
|
||||
<el-table-column label="轧辊编号" align="center" width="110">
|
||||
<template slot-scope="scope"><span class="perf-roll">{{ scope.row.rollNo || '—' }}</span></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工作长度(m)" align="center" width="100">
|
||||
<template slot-scope="scope">{{ scope.row.workLength != null ? scope.row.workLength : '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="过卷数" align="center" width="70">
|
||||
<template slot-scope="scope">{{ scope.row.coilCount != null ? scope.row.coilCount : '—' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ② 换辊历史 -->
|
||||
<el-card shadow="never" class="roll-table-card" style="margin-top:16px">
|
||||
<div slot="header" class="card-header">
|
||||
<span class="card-title"><i class="el-icon-document" /> 换辊历史</span>
|
||||
<el-form :inline="true" size="small" style="margin-left:auto;margin-bottom:0">
|
||||
<el-form-item label="类型" style="margin-bottom:0">
|
||||
<el-select v-model="historyQuery.changeType" placeholder="全部" clearable style="width:110px" @change="loadHistory">
|
||||
<el-option label="计划换辊" value="计划换辊" />
|
||||
<el-option label="紧急换辊" value="紧急换辊" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="时间起" style="margin-bottom:0">
|
||||
<el-date-picker v-model="historyQuery.changeTime" type="datetime"
|
||||
value-format="yyyy-MM-dd HH:mm:ss" placeholder="开始时间"
|
||||
style="width:160px" @change="loadHistory" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<KLPTable v-loading="historyLoading" :data="historyList">
|
||||
<el-table-column label="换辊编号" align="center" prop="changeNo" width="130" />
|
||||
<el-table-column label="换辊时间" align="center" prop="changeTime" width="160" />
|
||||
<el-table-column label="类型" align="center" prop="changeType" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="small" :type="scope.row.changeType === '紧急换辊' ? 'danger' : ''">{{ scope.row.changeType }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作人" align="center" prop="operator" width="80" />
|
||||
<el-table-column label="换辊辊组" align="left" min-width="160">
|
||||
<template slot-scope="scope">
|
||||
<div class="roll-group-cell">
|
||||
<span v-if="scope.row.upperBrNo" class="rg-item"><b>上BR</b> {{ scope.row.upperBrNo }}<em v-if="scope.row.upperBrDia"> φ{{ scope.row.upperBrDia }}</em></span>
|
||||
<span v-if="scope.row.upperWrNo" class="rg-item"><b>上WR</b> {{ scope.row.upperWrNo }}<em v-if="scope.row.upperWrDia"> φ{{ scope.row.upperWrDia }}</em></span>
|
||||
<span v-if="scope.row.lowerWrNo" class="rg-item"><b>下WR</b> {{ scope.row.lowerWrNo }}<em v-if="scope.row.lowerWrDia"> φ{{ scope.row.lowerWrDia }}</em></span>
|
||||
<span v-if="scope.row.lowerBrNo" class="rg-item"><b>下BR</b> {{ scope.row.lowerBrNo }}<em v-if="scope.row.lowerBrDia"> φ{{ scope.row.lowerBrDia }}</em></span>
|
||||
<span v-if="!scope.row.upperBrNo && !scope.row.upperWrNo && !scope.row.lowerWrNo && !scope.row.lowerBrNo" style="color:#c0c4cc">—</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="工作长度(m)" align="center" prop="workLength" width="100" />
|
||||
<el-table-column label="过卷数" align="center" prop="coilCount" width="72" />
|
||||
<el-table-column label="过卷重量" align="center" prop="totalWeight" width="90" />
|
||||
<el-table-column label="备注" align="left" prop="remark" min-width="100" show-overflow-tooltip />
|
||||
<el-table-column label="操作" align="center" width="110" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleEditHistory(scope.row)">补录</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" style="color:#c5221f"
|
||||
@click="handleDeleteHistory(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</KLPTable>
|
||||
<pagination
|
||||
v-show="historyTotal > 0"
|
||||
:total="historyTotal"
|
||||
:page.sync="historyQuery.pageNum"
|
||||
:limit.sync="historyQuery.pageSize"
|
||||
@pagination="loadHistory"
|
||||
/>
|
||||
</el-card>
|
||||
|
||||
<!-- 换辊确认弹窗 -->
|
||||
<el-dialog title="确认换辊" :visible.sync="changeOpen" width="420px" append-to-body @close="resetChangeForm">
|
||||
<div class="roll-preview">
|
||||
<div class="roll-preview__title">以下轧辊将被换入</div>
|
||||
<div class="roll-preview__item">
|
||||
<span class="rp-label">上支撑辊</span>
|
||||
<span class="rp-val">{{ changeForm.upperBrNo || '—' }}</span>
|
||||
<span class="rp-dia" v-if="changeForm.upperBrDia">φ{{ changeForm.upperBrDia }} mm</span>
|
||||
</div>
|
||||
<div class="roll-preview__item">
|
||||
<span class="rp-label">上工作辊</span>
|
||||
<span class="rp-val">{{ changeForm.upperWrNo || '—' }}</span>
|
||||
<span class="rp-dia" v-if="changeForm.upperWrDia">φ{{ changeForm.upperWrDia }} mm</span>
|
||||
</div>
|
||||
<div class="roll-preview__item">
|
||||
<span class="rp-label">下工作辊</span>
|
||||
<span class="rp-val">{{ changeForm.lowerWrNo || '—' }}</span>
|
||||
<span class="rp-dia" v-if="changeForm.lowerWrDia">φ{{ changeForm.lowerWrDia }} mm</span>
|
||||
</div>
|
||||
<div class="roll-preview__item">
|
||||
<span class="rp-label">下支撑辊</span>
|
||||
<span class="rp-val">{{ changeForm.lowerBrNo || '—' }}</span>
|
||||
<span class="rp-dia" v-if="changeForm.lowerBrDia">φ{{ changeForm.lowerBrDia }} mm</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="roll-preview__tip">换辊时间和操作人将由系统自动记录</div>
|
||||
<div slot="footer">
|
||||
<el-button type="primary" :loading="changeSubmitting" @click="submitChange">确认换辊</el-button>
|
||||
<el-button @click="changeOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 换辊历史补录/编辑对话框 -->
|
||||
<el-dialog title="换辊记录补录" :visible.sync="historyEditOpen" width="560px" append-to-body @close="historyEditForm = {}">
|
||||
<el-form ref="historyEditForm" :model="historyEditForm" label-width="100px" size="small">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="换辊类型">
|
||||
<el-select v-model="historyEditForm.changeType" style="width:100%">
|
||||
<el-option label="计划换辊" value="计划换辊" />
|
||||
<el-option label="紧急换辊" value="紧急换辊" />
|
||||
<el-option label="三级换辊" value="三级换辊" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="换辊时间">
|
||||
<el-date-picker v-model="historyEditForm.changeTime" type="datetime"
|
||||
value-format="yyyy-MM-dd HH:mm:ss" style="width:100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="操作人">
|
||||
<el-input v-model="historyEditForm.operator" />
|
||||
</el-form-item>
|
||||
<el-divider content-position="left">工作辊</el-divider>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="上工作辊"><el-input v-model="historyEditForm.upperWrNo" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="上工作辊径"><el-input-number v-model="historyEditForm.upperWrDia" :precision="2" :min="0" style="width:100%" /></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="下工作辊"><el-input v-model="historyEditForm.lowerWrNo" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="下工作辊径"><el-input-number v-model="historyEditForm.lowerWrDia" :precision="2" :min="0" style="width:100%" /></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-divider content-position="left">支撑辊</el-divider>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="上支撑辊"><el-input v-model="historyEditForm.upperBrNo" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="上支撑辊径"><el-input-number v-model="historyEditForm.upperBrDia" :precision="2" :min="0" style="width:100%" /></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="下支撑辊"><el-input v-model="historyEditForm.lowerBrNo" /></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="下支撑辊径"><el-input-number v-model="historyEditForm.lowerBrDia" :precision="2" :min="0" style="width:100%" /></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="historyEditForm.remark" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button type="primary" @click="submitHistoryEdit">保 存</el-button>
|
||||
<el-button @click="historyEditOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 添加下批轧辊对话框 -->
|
||||
<el-dialog title="添加下批轧辊" :visible.sync="standbyOpen" width="460px" append-to-body @close="resetStandbyForm">
|
||||
<el-form ref="standbyForm" :model="standbyForm" :rules="standbyRules" label-width="100px">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="辊型" prop="rollType">
|
||||
<el-input
|
||||
v-if="standbyFromCell"
|
||||
:value="standbyForm.rollType === 'WR' ? '工作辊 WR' : '支撑辊 BR'"
|
||||
readonly style="width:100%"
|
||||
/>
|
||||
<el-select v-else v-model="standbyForm.rollType" placeholder="请选择" style="width:100%"
|
||||
@change="handleStandbyRollTypeChange">
|
||||
<el-option label="工作辊 WR" value="WR" />
|
||||
<el-option label="支撑辊 BR" value="BR" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="辊位" prop="position">
|
||||
<el-input
|
||||
v-if="standbyFromCell"
|
||||
:value="standbyForm.position === 'UP' ? '上辊' : '下辊'"
|
||||
readonly style="width:100%"
|
||||
/>
|
||||
<el-select v-else v-model="standbyForm.position" placeholder="请选择" style="width:100%">
|
||||
<el-option label="上辊" value="UP" />
|
||||
<el-option label="下辊" value="DOWN" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="轧辊编号" prop="rollNo">
|
||||
<el-select v-model="standbyForm.rollNo" placeholder="请选择" filterable style="width:100%">
|
||||
<el-option
|
||||
v-for="no in standbyForm.rollType === 'WR' ? wrOptions : brOptions"
|
||||
:key="no" :label="no" :value="no"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="就绪时间">
|
||||
<el-date-picker v-model="standbyForm.readyTime" type="datetime"
|
||||
value-format="yyyy-MM-dd HH:mm:ss" placeholder="默认当前时间" style="width:100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="standbyForm.remark" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button type="primary" @click="submitStandby">确 定</el-button>
|
||||
<el-button @click="standbyOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCurrentRolls, listRollChange, addRollChange, updateRollChange, delRollChange, getRollPerformance } from '@/api/mes/roll/rollChange'
|
||||
import { listRollStandby, addRollStandby, delRollStandby, clearRollStandby } from '@/api/mes/roll/rollStandby'
|
||||
import { listRollOptions, listRollInfo } from '@/api/mes/roll/rollInfo'
|
||||
import rollLineMixin from '../rollLineMixin'
|
||||
|
||||
const STAND_NO = '1#'
|
||||
|
||||
const ParamCell = {
|
||||
name: 'ParamCell',
|
||||
props: { data: { type: Object, default: null } },
|
||||
render(h) {
|
||||
const d = this.data
|
||||
if (!d || d.val == null || d.val === '') return h('span', { class: 'cell-empty' }, '—')
|
||||
return h('span', { class: 'cell-main' }, String(d.val))
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'WorkingRollSingle',
|
||||
components: { ParamCell },
|
||||
mixins: [rollLineMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
current: {},
|
||||
loadingCurrent: false,
|
||||
|
||||
standbyList: [],
|
||||
loadingStandby: false,
|
||||
|
||||
rollInfoMap: {},
|
||||
|
||||
historyLoading: false,
|
||||
historyList: [],
|
||||
historyTotal: 0,
|
||||
historyQuery: { pageNum: 1, pageSize: 15, standNo: STAND_NO, changeType: undefined, changeTime: undefined },
|
||||
|
||||
wrOptions: [],
|
||||
brOptions: [],
|
||||
|
||||
historyEditOpen: false,
|
||||
historyEditForm: {},
|
||||
|
||||
offlineRolls: [],
|
||||
offlineLoading: false,
|
||||
|
||||
perfData: {},
|
||||
perfLoading: false,
|
||||
|
||||
asideHalfH: 200,
|
||||
|
||||
changeOpen: false,
|
||||
changeSubmitting: false,
|
||||
changeForm: {},
|
||||
|
||||
standbyOpen: false,
|
||||
standbyForm: {},
|
||||
standbyFromCell: false,
|
||||
standbyRules: {
|
||||
rollType: [{ required: true, message: '请选择辊型', trigger: 'change' }],
|
||||
position: [{ required: true, message: '请选择辊位', trigger: 'change' }],
|
||||
rollNo: [{ required: true, message: '请选择轧辊编号', trigger: 'change' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
mergedRows() {
|
||||
const c = this.current || {}
|
||||
const ri = this.rollInfoMap
|
||||
|
||||
const cv = (val, rollNo) => ({
|
||||
val: (val == null || val === '') ? null : val,
|
||||
sub: rollNo || null
|
||||
})
|
||||
const rv = (rollNo, field) => {
|
||||
const info = rollNo ? ri[rollNo] : null
|
||||
const val = info ? info[field] : null
|
||||
return { val: (val == null || val === '') ? null : val, sub: rollNo || null }
|
||||
}
|
||||
const sv = (rollType, pos, field) => {
|
||||
const item = this.standbyList.find(i => i.rollType === rollType && i.position === pos)
|
||||
if (!item) return { val: null, sub: null }
|
||||
let val = item[field]
|
||||
if ((val == null || val === '') && item.rollNo) {
|
||||
const info = ri[item.rollNo]
|
||||
if (info) {
|
||||
val = field === 'diameter'
|
||||
? (info.currentDia != null ? info.currentDia : info.initialDia)
|
||||
: info[field]
|
||||
}
|
||||
}
|
||||
return { val: (val == null || val === '') ? null : val, sub: item.rollNo, standbyId: item.standbyId }
|
||||
}
|
||||
const sm = (rollType, pos) => {
|
||||
const item = this.standbyList.find(i => i.rollType === rollType && i.position === pos)
|
||||
if (!item || !item.rollNo) return { val: null, sub: null }
|
||||
const info = ri[item.rollNo]
|
||||
return { val: info ? (info.material || null) : null, sub: item.rollNo, standbyId: item.standbyId }
|
||||
}
|
||||
|
||||
return [
|
||||
{ label: '上支承辊直径(mm)', group: 'br', rollType: 'BR', position: 'UP',
|
||||
cur: cv(c.upperBrDia, c.upperBrNo), sb: sv('BR', 'UP', 'diameter') },
|
||||
{ label: '上工作辊直径(mm)', group: 'wr', rollType: 'WR', position: 'UP',
|
||||
cur: cv(c.upperWrDia, c.upperWrNo), sb: sv('WR', 'UP', 'diameter') },
|
||||
{ label: '上工作辊凸度', group: 'wr', rollType: 'WR', position: 'UP',
|
||||
cur: rv(c.upperWrNo, 'crown'), sb: sv('WR', 'UP', 'crown') },
|
||||
{ label: '上工作辊粗糙度(μm)', group: 'wr', rollType: 'WR', position: 'UP',
|
||||
cur: rv(c.upperWrNo, 'roughness'), sb: sv('WR', 'UP', 'roughness') },
|
||||
{ label: '工作辊类型', group: 'wr', rollType: 'WR', position: 'UP',
|
||||
cur: rv(c.upperWrNo, 'material'), sb: sm('WR', 'UP') },
|
||||
{ label: '下工作辊直径(mm)', group: 'wr', rollType: 'WR', position: 'DOWN',
|
||||
cur: cv(c.lowerWrDia, c.lowerWrNo), sb: sv('WR', 'DOWN', 'diameter') },
|
||||
{ label: '下工作辊凸度', group: 'wr', rollType: 'WR', position: 'DOWN',
|
||||
cur: rv(c.lowerWrNo, 'crown'), sb: sv('WR', 'DOWN', 'crown') },
|
||||
{ label: '下工作辊粗糙度(μm)', group: 'wr', rollType: 'WR', position: 'DOWN',
|
||||
cur: rv(c.lowerWrNo, 'roughness'), sb: sv('WR', 'DOWN', 'roughness') },
|
||||
{ label: '下支承辊直径(mm)', group: 'br', rollType: 'BR', position: 'DOWN',
|
||||
cur: cv(c.lowerBrDia, c.lowerBrNo), sb: sv('BR', 'DOWN', 'diameter') }
|
||||
]
|
||||
},
|
||||
|
||||
perfRows() {
|
||||
const standData = (this.perfData['upperBr'] || {})
|
||||
const d = (key) => {
|
||||
const obj = (this.perfData[key] || {})[STAND_NO] || {}
|
||||
return { rollNo: obj.rollNo, workLength: obj.workLength, coilCount: obj.coilCount }
|
||||
}
|
||||
return [
|
||||
{ label: '上支撑辊', ...d('upperBr') },
|
||||
{ label: '上工作辊', ...d('upperWr') },
|
||||
{ label: '下工作辊', ...d('lowerWr') },
|
||||
{ label: '下支撑辊', ...d('lowerBr') }
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
created() {},
|
||||
|
||||
mounted() {
|
||||
this.$nextTick(this.syncAsideHeight)
|
||||
window.addEventListener('resize', this.syncAsideHeight)
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.syncAsideHeight)
|
||||
},
|
||||
|
||||
methods: {
|
||||
onLineResolved() {
|
||||
this.loadCurrent()
|
||||
this.loadStandby()
|
||||
this.loadHistory()
|
||||
this.loadOfflineRolls()
|
||||
this.loadRollPerformance()
|
||||
this.loadRollOptions()
|
||||
this.loadRollInfoMap()
|
||||
},
|
||||
|
||||
loadCurrent() {
|
||||
this.loadingCurrent = true
|
||||
getCurrentRolls(this.lineId, STAND_NO).then(res => {
|
||||
this.current = res.data || {}
|
||||
this.$nextTick(this.syncAsideHeight)
|
||||
}).finally(() => { this.loadingCurrent = false })
|
||||
},
|
||||
|
||||
loadStandby() {
|
||||
this.loadingStandby = true
|
||||
listRollStandby(this.lineId, STAND_NO).then(res => {
|
||||
this.standbyList = res.data || []
|
||||
}).finally(() => { this.loadingStandby = false })
|
||||
},
|
||||
|
||||
loadHistory() {
|
||||
this.historyLoading = true
|
||||
listRollChange({ ...this.historyQuery, lineId: this.lineId }).then(res => {
|
||||
this.historyList = res.rows || []
|
||||
this.historyTotal = res.total || 0
|
||||
}).finally(() => { this.historyLoading = false })
|
||||
},
|
||||
|
||||
loadRollOptions() {
|
||||
listRollOptions(this.lineId, 'WR', 'Offline').then(res => { this.wrOptions = res.data || [] })
|
||||
listRollOptions(this.lineId, 'BR', 'Offline').then(res => { this.brOptions = res.data || [] })
|
||||
},
|
||||
|
||||
loadRollInfoMap() {
|
||||
listRollInfo({ lineId: this.lineId, pageNum: 1, pageSize: 500 }).then(res => {
|
||||
const map = {}
|
||||
;(res.rows || []).forEach(r => { map[r.rollNo] = r })
|
||||
this.rollInfoMap = map
|
||||
})
|
||||
},
|
||||
|
||||
loadRollPerformance() {
|
||||
this.perfLoading = true
|
||||
getRollPerformance(this.lineId).then(res => {
|
||||
this.perfData = res.data || {}
|
||||
}).finally(() => { this.perfLoading = false })
|
||||
},
|
||||
|
||||
loadOfflineRolls() {
|
||||
this.offlineLoading = true
|
||||
listRollInfo({ lineId: this.lineId, status: 'Offline', pageNum: 1, pageSize: 30 }).then(res => {
|
||||
this.offlineRolls = res.rows || []
|
||||
}).finally(() => { this.offlineLoading = false })
|
||||
},
|
||||
|
||||
rowClass({ row }) {
|
||||
if (row.group === 'br') return 'row-br'
|
||||
return ''
|
||||
},
|
||||
|
||||
mergeParamCol({ columnIndex }) {
|
||||
if (columnIndex === 0) return [1, 1]
|
||||
},
|
||||
|
||||
handleOpenChange() {
|
||||
const ri = this.rollInfoMap
|
||||
const pick = (rollType, pos) => {
|
||||
const item = this.standbyList.find(i => i.rollType === rollType && i.position === pos)
|
||||
if (!item) return { no: undefined, dia: undefined }
|
||||
const info = ri[item.rollNo]
|
||||
const dia = item.diameter != null ? item.diameter
|
||||
: (info ? (info.currentDia != null ? info.currentDia : info.initialDia) : undefined)
|
||||
return { no: item.rollNo, dia }
|
||||
}
|
||||
const uwr = pick('WR', 'UP'), lwr = pick('WR', 'DOWN')
|
||||
const ubr = pick('BR', 'UP'), lbr = pick('BR', 'DOWN')
|
||||
this.changeForm = {
|
||||
standNo: STAND_NO,
|
||||
lineId: this.lineId,
|
||||
changeType: undefined,
|
||||
upperWrNo: uwr.no, upperWrDia: uwr.dia,
|
||||
lowerWrNo: lwr.no, lowerWrDia: lwr.dia,
|
||||
upperBrNo: ubr.no, upperBrDia: ubr.dia,
|
||||
lowerBrNo: lbr.no, lowerBrDia: lbr.dia
|
||||
}
|
||||
this.changeOpen = true
|
||||
},
|
||||
|
||||
submitChange() {
|
||||
if (this.changeSubmitting) return
|
||||
this.changeSubmitting = true
|
||||
addRollChange(this.changeForm).then(() => {
|
||||
this.$modal.msgSuccess('换辊成功')
|
||||
this.changeOpen = false
|
||||
clearRollStandby(this.lineId, STAND_NO).finally(() => {
|
||||
this.loadCurrent()
|
||||
this.loadStandby()
|
||||
this.loadHistory()
|
||||
this.loadOfflineRolls()
|
||||
this.loadRollPerformance()
|
||||
})
|
||||
}).finally(() => { this.changeSubmitting = false })
|
||||
},
|
||||
|
||||
resetChangeForm() {
|
||||
this.changeForm = {}
|
||||
this.changeSubmitting = false
|
||||
},
|
||||
|
||||
handleSbCellClick(cell, rollType, position) {
|
||||
if (cell && cell.standbyId) {
|
||||
this.handleDelStandby(cell.standbyId)
|
||||
} else if (rollType) {
|
||||
this.standbyForm = { standNo: STAND_NO, rollType, position, rollNo: undefined }
|
||||
this.standbyFromCell = true
|
||||
this.standbyOpen = true
|
||||
}
|
||||
},
|
||||
|
||||
handleAddStandby() {
|
||||
this.standbyForm = { standNo: STAND_NO, rollType: undefined, position: undefined, rollNo: undefined }
|
||||
this.standbyFromCell = false
|
||||
this.standbyOpen = true
|
||||
},
|
||||
|
||||
handleStandbyRollTypeChange() {
|
||||
this.$set(this.standbyForm, 'rollNo', undefined)
|
||||
},
|
||||
|
||||
submitStandby() {
|
||||
this.$refs.standbyForm.validate(valid => {
|
||||
if (!valid) return
|
||||
addRollStandby({ ...this.standbyForm, lineId: this.lineId }).then(() => {
|
||||
this.$modal.msgSuccess('已添加到下批轧辊')
|
||||
this.standbyOpen = false
|
||||
this.loadStandby()
|
||||
this.loadRollOptions()
|
||||
this.loadOfflineRolls()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
resetStandbyForm() {
|
||||
this.standbyForm = {}
|
||||
this.standbyFromCell = false
|
||||
this.$nextTick(() => { this.$refs.standbyForm && this.$refs.standbyForm.clearValidate() })
|
||||
},
|
||||
|
||||
handleDelStandby(standbyId) {
|
||||
this.$modal.confirm('确认移除该下批轧辊?移除后该辊状态将恢复为"离线"。').then(() => {
|
||||
return delRollStandby(standbyId)
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('已移除')
|
||||
this.loadStandby()
|
||||
this.loadRollOptions()
|
||||
this.loadOfflineRolls()
|
||||
})
|
||||
},
|
||||
|
||||
handleClearStandby() {
|
||||
this.$modal.confirm('确认清空全部下批轧辊?').then(() => {
|
||||
return clearRollStandby(this.lineId, STAND_NO)
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('已清空')
|
||||
this.loadStandby()
|
||||
})
|
||||
},
|
||||
|
||||
handleEditHistory(row) {
|
||||
this.historyEditForm = { ...row }
|
||||
this.historyEditOpen = true
|
||||
},
|
||||
|
||||
submitHistoryEdit() {
|
||||
updateRollChange(this.historyEditForm).then(() => {
|
||||
this.$modal.msgSuccess('保存成功')
|
||||
this.historyEditOpen = false
|
||||
this.loadHistory()
|
||||
})
|
||||
},
|
||||
|
||||
handleDeleteHistory(row) {
|
||||
this.$modal.confirm('确认删除该换辊记录?此操作不可恢复。').then(() => {
|
||||
return delRollChange(row.changeId)
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('已删除')
|
||||
this.loadHistory()
|
||||
})
|
||||
},
|
||||
|
||||
syncAsideHeight() {
|
||||
const el = this.$refs.mainTable && this.$refs.mainTable.$el
|
||||
if (!el) return
|
||||
const totalH = el.offsetHeight
|
||||
const gap = 12
|
||||
const headerH = 48
|
||||
const half = Math.max(100, Math.floor((totalH - gap) / 2) - headerH)
|
||||
this.asideHalfH = half
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.working-roll-page { background: #f4f5f7; }
|
||||
.roll-table-card { border: 1px solid #dcdee0; border-radius: 4px; }
|
||||
|
||||
.top-row { display: flex; gap: 12px; align-items: stretch; }
|
||||
.top-row__main { flex: 2; min-width: 0; }
|
||||
.top-row__aside { flex: 1; min-width: 0; }
|
||||
|
||||
.aside-col { display: flex; flex-direction: column; gap: 12px; }
|
||||
.aside-panel { flex: 1; min-width: 0; }
|
||||
|
||||
.roll-group-cell { display: flex; flex-wrap: wrap; gap: 4px; line-height: 1.5; }
|
||||
.rg-item { font-size: 12px; color: #3d4b5c; }
|
||||
.rg-item b { color: #5f6368; font-weight: 600; margin-right: 2px; }
|
||||
.rg-item em { font-style: normal; color: #9aa0a6; font-size: 11px; }
|
||||
|
||||
.card-header { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.card-title { font-size: 13px; font-weight: 600; color: #3d4b5c; white-space: nowrap; }
|
||||
.header-meta { font-size: 11px; color: #8f9099; white-space: nowrap; }
|
||||
|
||||
.param-label { font-size: 12px; color: #3d4b5c; }
|
||||
.cell-main { font-family: 'Consolas', 'Courier New', monospace; font-size: 13px; font-weight: 600; color: #1f2329; }
|
||||
.cell-empty { color: #c0c4cc; font-size: 12px; }
|
||||
|
||||
.perf-roll { font-family: 'Consolas', monospace; font-size: 12px; font-weight: 600; color: #1f2329; }
|
||||
|
||||
.roll-preview { background: #f7f8fa; border: 1px solid #e4e6eb; border-radius: 4px; padding: 10px 12px; }
|
||||
.roll-preview__title { font-size: 11px; color: #8f9099; margin-bottom: 8px; letter-spacing: .5px; }
|
||||
.roll-preview__item { display: flex; align-items: center; gap: 6px; padding: 4px 0; }
|
||||
.rp-label { font-size: 12px; color: #8f9099; width: 56px; flex-shrink: 0; }
|
||||
.rp-val { font-size: 13px; font-weight: 600; color: #1f2329; font-family: 'Consolas', monospace; flex: 1; }
|
||||
.rp-dia { font-size: 11px; color: #9aa0a6; }
|
||||
.roll-preview__tip { font-size: 11px; color: #b0b3bb; margin-top: 10px; text-align: center; }
|
||||
|
||||
.sb-cell { display: block; min-height: 24px; border-radius: 2px; cursor: default; }
|
||||
.sb-cell--add { cursor: pointer; background: #f0f9f4; }
|
||||
.sb-cell--add:hover { background: #d4edda; }
|
||||
.sb-cell--del { cursor: pointer; background: #fff7f7; }
|
||||
.sb-cell--del:hover { background: #ffe4e4; }
|
||||
|
||||
::v-deep .row-br td { background: #fafafa !important; }
|
||||
</style>
|
||||
|
||||
<style>
|
||||
.el-table .editing-row { background: #fffbf0 !important; }
|
||||
</style>
|
||||
@@ -381,6 +381,7 @@
|
||||
import { getCurrentRolls, listRollChange, addRollChange, updateRollChange, delRollChange, getRollPerformance } from '@/api/mes/roll/rollChange'
|
||||
import { listRollStandby, addRollStandby, delRollStandby, clearRollStandby } from '@/api/mes/roll/rollStandby'
|
||||
import { listRollOptions, listRollInfo } from '@/api/mes/roll/rollInfo'
|
||||
import rollLineMixin from '../rollLineMixin'
|
||||
|
||||
const ParamCell = {
|
||||
name: 'ParamCell',
|
||||
@@ -409,6 +410,7 @@ const PerfCell = {
|
||||
export default {
|
||||
name: 'WorkingRoll',
|
||||
components: { ParamCell, PerfCell },
|
||||
mixins: [rollLineMixin],
|
||||
data() {
|
||||
return {
|
||||
current: { '1#': {}, '2#': {} },
|
||||
@@ -576,11 +578,7 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.loadAll()
|
||||
this.loadRollOptions()
|
||||
this.loadRollInfoMap()
|
||||
},
|
||||
created() {},
|
||||
|
||||
mounted() {
|
||||
this.$nextTick(this.syncAsideHeight)
|
||||
@@ -592,6 +590,12 @@ export default {
|
||||
},
|
||||
|
||||
methods: {
|
||||
onLineResolved() {
|
||||
this.loadAll()
|
||||
this.loadRollOptions()
|
||||
this.loadRollInfoMap()
|
||||
},
|
||||
|
||||
getDefaultChangeForm(standNo) {
|
||||
return {
|
||||
standNo: standNo || undefined,
|
||||
@@ -631,14 +635,14 @@ export default {
|
||||
|
||||
loadRollPerformance() {
|
||||
this.perfLoading = true
|
||||
getRollPerformance().then(res => {
|
||||
getRollPerformance(this.lineId).then(res => {
|
||||
this.perfData = res.data || {}
|
||||
}).finally(() => { this.perfLoading = false })
|
||||
},
|
||||
|
||||
loadCurrent(standNo) {
|
||||
this.$set(this.loadingCurrent, standNo, true)
|
||||
getCurrentRolls(standNo).then(res => {
|
||||
getCurrentRolls(this.lineId, standNo).then(res => {
|
||||
this.$set(this.current, standNo, res.data || {})
|
||||
this.$set(this.loadingCurrent, standNo, false)
|
||||
this.$nextTick(this.syncAsideHeight)
|
||||
@@ -647,7 +651,7 @@ export default {
|
||||
|
||||
loadStandby(standNo) {
|
||||
this.$set(this.loadingStandby, standNo, true)
|
||||
listRollStandby(standNo).then(res => {
|
||||
listRollStandby(this.lineId, standNo).then(res => {
|
||||
this.$set(this.standbyList, standNo, res.data || [])
|
||||
this.$set(this.loadingStandby, standNo, false)
|
||||
}).catch(() => { this.$set(this.loadingStandby, standNo, false) })
|
||||
@@ -655,7 +659,7 @@ export default {
|
||||
|
||||
loadHistory() {
|
||||
this.historyLoading = true
|
||||
listRollChange(this.historyQuery).then(res => {
|
||||
listRollChange({ ...this.historyQuery, lineId: this.lineId }).then(res => {
|
||||
this.historyList = res.rows || []
|
||||
this.historyTotal = res.total || 0
|
||||
this.historyLoading = false
|
||||
@@ -663,13 +667,13 @@ export default {
|
||||
},
|
||||
|
||||
loadRollOptions() {
|
||||
// 下批辊选择只显示离线(Offline)状态的轧辊
|
||||
listRollOptions('WR', 'Offline').then(res => { this.wrOptions = res.data || [] })
|
||||
listRollOptions('BR', 'Offline').then(res => { this.brOptions = res.data || [] })
|
||||
// 下批辊选择只显示离线(Offline)状态的轧辊,限定当前产线
|
||||
listRollOptions(this.lineId, 'WR', 'Offline').then(res => { this.wrOptions = res.data || [] })
|
||||
listRollOptions(this.lineId, 'BR', 'Offline').then(res => { this.brOptions = res.data || [] })
|
||||
},
|
||||
|
||||
loadRollInfoMap() {
|
||||
listRollInfo({ pageNum: 1, pageSize: 500 }).then(res => {
|
||||
listRollInfo({ lineId: this.lineId, pageNum: 1, pageSize: 500 }).then(res => {
|
||||
const map = {}
|
||||
;(res.rows || []).forEach(r => { map[r.rollNo] = r })
|
||||
this.rollInfoMap = map
|
||||
@@ -708,6 +712,7 @@ export default {
|
||||
|
||||
this.changeForm = {
|
||||
...this.getDefaultChangeForm(standNo),
|
||||
lineId: this.lineId,
|
||||
upperWrNo: uwr.no, upperWrDia: uwr.dia,
|
||||
lowerWrNo: lwr.no, lowerWrDia: lwr.dia,
|
||||
upperBrNo: ubr.no, upperBrDia: ubr.dia,
|
||||
@@ -722,7 +727,7 @@ export default {
|
||||
addRollChange(this.changeForm).then(() => {
|
||||
this.$modal.msgSuccess('换辊成功')
|
||||
this.changeOpen = false
|
||||
clearRollStandby(standNo).finally(() => {
|
||||
clearRollStandby(this.lineId, standNo).finally(() => {
|
||||
this.loadCurrent(standNo)
|
||||
this.loadStandby(standNo)
|
||||
this.loadHistory()
|
||||
@@ -761,7 +766,7 @@ export default {
|
||||
submitStandby() {
|
||||
this.$refs.standbyForm.validate(valid => {
|
||||
if (!valid) return
|
||||
addRollStandby(this.standbyForm).then(() => {
|
||||
addRollStandby({ ...this.standbyForm, lineId: this.lineId }).then(() => {
|
||||
this.$modal.msgSuccess('已添加到下批轧辊')
|
||||
this.standbyOpen = false
|
||||
this.loadStandby(this.standbyForm.standNo)
|
||||
@@ -787,7 +792,7 @@ export default {
|
||||
},
|
||||
handleClearStandby(standNo) {
|
||||
this.$modal.confirm('确认清空 ' + standNo + ' 机架的全部下批轧辊?').then(() => {
|
||||
return clearRollStandby(standNo)
|
||||
return clearRollStandby(this.lineId, standNo)
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('已清空')
|
||||
this.loadStandby(standNo)
|
||||
@@ -818,7 +823,7 @@ export default {
|
||||
// ── 可用离线辊列表 ────────────────────────────────────────
|
||||
loadOfflineRolls() {
|
||||
this.offlineLoading = true
|
||||
listRollInfo({ status: 'Offline', pageNum: 1, pageSize: 30 }).then(res => {
|
||||
listRollInfo({ lineId: this.lineId, status: 'Offline', pageNum: 1, pageSize: 30 }).then(res => {
|
||||
this.offlineRolls = res.rows || []
|
||||
}).finally(() => {
|
||||
this.offlineLoading = false
|
||||
|
||||
247
klp-ui/src/views/wms/coil/do/acid-merge.vue
Normal file
247
klp-ui/src/views/wms/coil/do/acid-merge.vue
Normal file
@@ -0,0 +1,247 @@
|
||||
<template>
|
||||
<div class="app-container acid-op-page">
|
||||
<el-row :gutter="16">
|
||||
|
||||
<!-- ── 左侧:合卷表单 ── -->
|
||||
<el-col :span="15">
|
||||
<div class="op-card">
|
||||
<div class="op-header">
|
||||
<span class="op-title">合卷操作</span>
|
||||
<el-tag size="mini" type="info" style="margin-left:8px">actionType = 200</el-tag>
|
||||
</div>
|
||||
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="110px" size="small">
|
||||
|
||||
<!-- 源卷列表(动态) -->
|
||||
<div class="sub-section-label">源卷列表</div>
|
||||
<div v-for="(item, idx) in form.sourceCoils" :key="idx" class="source-row">
|
||||
<el-form-item
|
||||
:label="'源卷 ' + (idx + 1)"
|
||||
:prop="'sourceCoils.' + idx + '.enterCoilNo'"
|
||||
:rules="[{ required: true, message: '请输入源卷号', trigger: 'blur' }]"
|
||||
style="margin-bottom:8px"
|
||||
>
|
||||
<el-input v-model="item.enterCoilNo" :placeholder="'入场钢卷号 ' + (idx + 1)"
|
||||
clearable style="width:calc(100% - 36px)"
|
||||
@keyup.enter.native="onSourceCoilInput(item)"
|
||||
@blur="onSourceCoilInput(item)" />
|
||||
<el-button icon="el-icon-minus" size="mini" type="danger" circle
|
||||
style="margin-left:6px" :disabled="form.sourceCoils.length <= 1"
|
||||
@click="removeSourceCoil(idx)" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-button size="mini" type="primary" plain icon="el-icon-plus"
|
||||
style="margin-left:110px;margin-bottom:12px" @click="addSourceCoil">
|
||||
添加源卷
|
||||
</el-button>
|
||||
|
||||
<el-divider content-position="left" style="margin:4px 0 12px">合卷结果</el-divider>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="合卷后卷号" prop="targetCoilNo">
|
||||
<el-input v-model="form.targetCoilNo" placeholder="请输入合卷后钢卷号" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="合卷后重量(t)" prop="mergedWeight">
|
||||
<el-input-number v-model="form.mergedWeight" :precision="3" :min="0"
|
||||
:controls="false" style="width:100%" placeholder="—" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="出口厚度(mm)" prop="exitThickness">
|
||||
<el-input-number v-model="form.exitThickness" :precision="3" :min="0"
|
||||
:controls="false" style="width:100%" placeholder="—" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="出口宽度(mm)" prop="exitWidth">
|
||||
<el-input-number v-model="form.exitWidth" :precision="1" :min="0"
|
||||
:controls="false" style="width:100%" placeholder="—" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="班组" prop="team">
|
||||
<el-select v-model="form.team" placeholder="请选择" style="width:100%" clearable>
|
||||
<el-option label="甲班" value="甲" />
|
||||
<el-option label="乙班" value="乙" />
|
||||
<el-option label="丙班" value="丙" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" placeholder="选填" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div style="text-align:right;padding-top:4px">
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="submitForm">确认合卷</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 历史记录 -->
|
||||
<div class="op-card" style="margin-top:12px">
|
||||
<div class="op-header">
|
||||
<span class="op-title">最近合卷记录</span>
|
||||
<el-button size="mini" icon="el-icon-refresh" style="margin-left:auto" @click="loadHistory">刷新</el-button>
|
||||
</div>
|
||||
<el-table v-loading="historyLoading" :data="historyList" size="mini" border style="width:100%">
|
||||
<el-table-column prop="enterCoilNo" label="源卷号" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="currentCoilNo" label="合卷后卷号" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="exitThickness" label="出口厚(mm)" width="90" align="right" />
|
||||
<el-table-column prop="exitWidth" label="出口宽(mm)" width="90" align="right" />
|
||||
<el-table-column prop="netWeight" label="重量(t)" width="80" align="right" />
|
||||
<el-table-column prop="team" label="班组" width="60" align="center" />
|
||||
<el-table-column prop="createTime" label="录入时间" width="150" />
|
||||
</el-table>
|
||||
<pagination v-show="historyTotal > 0" :total="historyTotal"
|
||||
:page.sync="historyQuery.pageNum" :limit.sync="historyQuery.pageSize"
|
||||
@pagination="loadHistory" style="margin-top:6px" />
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- ── 右侧:L2 匹配面板(以第一个源卷为锚点) ── -->
|
||||
<el-col :span="9">
|
||||
<l2-match-panel :hot-coil-id="l2HotCoilId" @fill="applyL2Fill" />
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addCoilWarehouseOperationLog, listCoilWarehouseOperationLog } from '@/api/wms/coilWarehouseOperationLog'
|
||||
import L2MatchPanel from '../panels/L2MatchPanel.vue'
|
||||
|
||||
export default {
|
||||
name: 'AcidMerge',
|
||||
components: { L2MatchPanel },
|
||||
data() {
|
||||
return {
|
||||
form: this.defaultForm(),
|
||||
rules: {
|
||||
targetCoilNo: [{ required: true, message: '合卷后卷号不能为空', trigger: 'blur' }],
|
||||
},
|
||||
submitting: false,
|
||||
l2HotCoilId: '',
|
||||
|
||||
historyLoading: false,
|
||||
historyList: [],
|
||||
historyTotal: 0,
|
||||
historyQuery: { pageNum: 1, pageSize: 10, actionType: 200 },
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadHistory()
|
||||
},
|
||||
methods: {
|
||||
defaultForm() {
|
||||
return {
|
||||
sourceCoils: [{ enterCoilNo: '' }],
|
||||
targetCoilNo: '',
|
||||
mergedWeight: undefined,
|
||||
exitThickness: undefined,
|
||||
exitWidth: undefined,
|
||||
team: undefined,
|
||||
remark: '',
|
||||
actionType: 200,
|
||||
}
|
||||
},
|
||||
|
||||
addSourceCoil() {
|
||||
this.form.sourceCoils.push({ enterCoilNo: '' })
|
||||
},
|
||||
removeSourceCoil(idx) {
|
||||
this.form.sourceCoils.splice(idx, 1)
|
||||
// 重新推算锚点
|
||||
this.l2HotCoilId = (this.form.sourceCoils[0]?.enterCoilNo || '').trim()
|
||||
},
|
||||
onSourceCoilInput(item) {
|
||||
// 以列表第一个源卷的卷号作为 L2 查询锚点
|
||||
const first = (this.form.sourceCoils[0]?.enterCoilNo || '').trim()
|
||||
this.l2HotCoilId = first
|
||||
},
|
||||
|
||||
applyL2Fill(data) {
|
||||
if (data.exit_thick != null) this.form.exitThickness = parseFloat(data.exit_thick)
|
||||
if (data.exit_width != null) this.form.exitWidth = parseFloat(data.exit_width)
|
||||
if (data.entry_weight != null) this.form.mergedWeight = parseFloat(data.entry_weight)
|
||||
this.$message.success('L2 数据已写入表单')
|
||||
},
|
||||
|
||||
submitForm() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (!valid) return
|
||||
this.submitting = true
|
||||
// 将源卷列表序列化放入 remark 或 enterCoilNo(多对一合卷用逗号分隔)
|
||||
const sourceNos = this.form.sourceCoils.map(s => s.enterCoilNo).filter(Boolean).join(',')
|
||||
const payload = {
|
||||
enterCoilNo: sourceNos,
|
||||
currentCoilNo: this.form.targetCoilNo,
|
||||
exitThickness: this.form.exitThickness,
|
||||
exitWidth: this.form.exitWidth,
|
||||
netWeight: this.form.mergedWeight,
|
||||
team: this.form.team,
|
||||
remark: this.form.remark,
|
||||
actionType: 200,
|
||||
}
|
||||
addCoilWarehouseOperationLog(payload).then(() => {
|
||||
this.$modal.msgSuccess('合卷操作已记录')
|
||||
this.resetForm()
|
||||
this.loadHistory()
|
||||
}).finally(() => { this.submitting = false })
|
||||
})
|
||||
},
|
||||
|
||||
resetForm() {
|
||||
this.form = this.defaultForm()
|
||||
this.l2HotCoilId = ''
|
||||
this.$nextTick(() => { this.$refs.form && this.$refs.form.clearValidate() })
|
||||
},
|
||||
|
||||
loadHistory() {
|
||||
this.historyLoading = true
|
||||
listCoilWarehouseOperationLog(this.historyQuery).then(res => {
|
||||
this.historyList = res.rows || []
|
||||
this.historyTotal = res.total || 0
|
||||
}).finally(() => { this.historyLoading = false })
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.acid-op-page { background: #f5f7fa; }
|
||||
.op-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
padding: 16px;
|
||||
}
|
||||
.op-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #67c23a;
|
||||
}
|
||||
.op-title { font-size: 15px; font-weight: 600; color: #303133; }
|
||||
.sub-section-label {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin: 0 0 8px 110px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.source-row { position: relative; }
|
||||
</style>
|
||||
237
klp-ui/src/views/wms/coil/do/acid-normal.vue
Normal file
237
klp-ui/src/views/wms/coil/do/acid-normal.vue
Normal file
@@ -0,0 +1,237 @@
|
||||
<template>
|
||||
<div class="app-container acid-op-page">
|
||||
<el-row :gutter="16">
|
||||
|
||||
<!-- ── 左侧:操作表单 + 历史记录 ── -->
|
||||
<el-col :span="15">
|
||||
|
||||
<!-- 新增表单 -->
|
||||
<div class="op-card">
|
||||
<div class="op-header">
|
||||
<span class="op-title">酸轧操作录入</span>
|
||||
<el-tag size="mini" type="info" style="margin-left:8px">actionType = 11</el-tag>
|
||||
</div>
|
||||
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="110px" size="small">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="入场钢卷号" prop="enterCoilNo">
|
||||
<el-input v-model="form.enterCoilNo" placeholder="回车自动查询 L2"
|
||||
clearable @keyup.enter.native="onEnterCoilInput"
|
||||
@blur="onEnterCoilInput" @clear="clearL2" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="出口钢卷号" prop="currentCoilNo">
|
||||
<el-input v-model="form.currentCoilNo" placeholder="请输入出口钢卷号" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left" style="margin:8px 0 12px">出口实绩</el-divider>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出口厚度(mm)" prop="exitThickness">
|
||||
<el-input-number v-model="form.exitThickness" :precision="3" :min="0"
|
||||
:controls="false" style="width:100%" placeholder="—" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出口宽度(mm)" prop="exitWidth">
|
||||
<el-input-number v-model="form.exitWidth" :precision="1" :min="0"
|
||||
:controls="false" style="width:100%" placeholder="—" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出口长度(m)" prop="exitLength">
|
||||
<el-input-number v-model="form.exitLength" :precision="1" :min="0"
|
||||
:controls="false" style="width:100%" placeholder="—" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="入口重量(t)" prop="entryWeight">
|
||||
<el-input-number v-model="form.entryWeight" :precision="3" :min="0"
|
||||
:controls="false" style="width:100%" placeholder="—" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出口重量(t)" prop="exitWeight">
|
||||
<el-input-number v-model="form.exitWeight" :precision="3" :min="0"
|
||||
:controls="false" style="width:100%" placeholder="—" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="工艺编码" prop="processCode">
|
||||
<el-input v-model="form.processCode" placeholder="—" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="班组" prop="team">
|
||||
<el-select v-model="form.team" placeholder="请选择" style="width:100%" clearable>
|
||||
<el-option label="甲班" value="甲" />
|
||||
<el-option label="乙班" value="乙" />
|
||||
<el-option label="丙班" value="丙" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" placeholder="选填" clearable />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div style="text-align:right;padding-top:4px">
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="submitForm">新增录入</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 历史记录 -->
|
||||
<div class="op-card" style="margin-top:12px">
|
||||
<div class="op-header">
|
||||
<span class="op-title">最近录入记录</span>
|
||||
<el-button size="mini" icon="el-icon-refresh" style="margin-left:auto" @click="loadHistory">刷新</el-button>
|
||||
</div>
|
||||
<el-table v-loading="historyLoading" :data="historyList" size="mini" border style="width:100%">
|
||||
<el-table-column prop="enterCoilNo" label="入场钢卷号" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="currentCoilNo" label="出口钢卷号" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="exitThickness" label="出口厚(mm)" width="90" align="right" />
|
||||
<el-table-column prop="exitWidth" label="出口宽(mm)" width="90" align="right" />
|
||||
<el-table-column prop="exitLength" label="长度(m)" width="80" align="right" />
|
||||
<el-table-column prop="entryWeight" label="入口重(t)" width="80" align="right" />
|
||||
<el-table-column prop="team" label="班组" width="60" align="center" />
|
||||
<el-table-column prop="createTime" label="录入时间" width="150" />
|
||||
</el-table>
|
||||
<pagination v-show="historyTotal > 0" :total="historyTotal"
|
||||
:page.sync="historyQuery.pageNum" :limit.sync="historyQuery.pageSize"
|
||||
@pagination="loadHistory" style="margin-top:6px" />
|
||||
</div>
|
||||
|
||||
</el-col>
|
||||
|
||||
<!-- ── 右侧:L2 匹配面板 ── -->
|
||||
<el-col :span="9">
|
||||
<l2-match-panel :hot-coil-id="l2HotCoilId" @fill="applyL2Fill" />
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addCoilWarehouseOperationLog, listCoilWarehouseOperationLog } from '@/api/wms/coilWarehouseOperationLog'
|
||||
import L2MatchPanel from '../panels/L2MatchPanel.vue'
|
||||
|
||||
export default {
|
||||
name: 'AcidNormal',
|
||||
components: { L2MatchPanel },
|
||||
data() {
|
||||
return {
|
||||
form: this.defaultForm(),
|
||||
rules: {
|
||||
enterCoilNo: [{ required: true, message: '入场钢卷号不能为空', trigger: 'blur' }],
|
||||
},
|
||||
submitting: false,
|
||||
l2HotCoilId: '',
|
||||
|
||||
historyLoading: false,
|
||||
historyList: [],
|
||||
historyTotal: 0,
|
||||
historyQuery: { pageNum: 1, pageSize: 10, actionType: 11 },
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadHistory()
|
||||
},
|
||||
methods: {
|
||||
defaultForm() {
|
||||
return {
|
||||
enterCoilNo: '',
|
||||
currentCoilNo: '',
|
||||
exitThickness: undefined,
|
||||
exitWidth: undefined,
|
||||
exitLength: undefined,
|
||||
entryWeight: undefined,
|
||||
exitWeight: undefined,
|
||||
processCode: '',
|
||||
team: undefined,
|
||||
remark: '',
|
||||
actionType: 11,
|
||||
}
|
||||
},
|
||||
|
||||
onEnterCoilInput() {
|
||||
const v = (this.form.enterCoilNo || '').trim()
|
||||
this.l2HotCoilId = v || ''
|
||||
},
|
||||
|
||||
clearL2() {
|
||||
this.l2HotCoilId = ''
|
||||
},
|
||||
|
||||
applyL2Fill(data) {
|
||||
if (data.exit_thick != null) this.form.exitThickness = parseFloat(data.exit_thick)
|
||||
if (data.exit_width != null) this.form.exitWidth = parseFloat(data.exit_width)
|
||||
if (data.exit_length != null) this.form.exitLength = parseFloat(data.exit_length)
|
||||
if (data.entry_weight != null) this.form.entryWeight = parseFloat(data.entry_weight)
|
||||
if (data.process_code) this.form.processCode = data.process_code
|
||||
this.$message.success('L2 数据已写入表单')
|
||||
},
|
||||
|
||||
submitForm() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (!valid) return
|
||||
this.submitting = true
|
||||
addCoilWarehouseOperationLog({ ...this.form }).then(() => {
|
||||
this.$modal.msgSuccess('录入成功')
|
||||
this.resetForm()
|
||||
this.loadHistory()
|
||||
}).finally(() => { this.submitting = false })
|
||||
})
|
||||
},
|
||||
|
||||
resetForm() {
|
||||
const coilNo = this.form.enterCoilNo
|
||||
this.form = this.defaultForm()
|
||||
this.$nextTick(() => {
|
||||
this.$refs.form && this.$refs.form.clearValidate()
|
||||
})
|
||||
},
|
||||
|
||||
loadHistory() {
|
||||
this.historyLoading = true
|
||||
listCoilWarehouseOperationLog(this.historyQuery).then(res => {
|
||||
this.historyList = res.rows || []
|
||||
this.historyTotal = res.total || 0
|
||||
}).finally(() => { this.historyLoading = false })
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.acid-op-page { background: #f5f7fa; }
|
||||
.op-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
padding: 16px;
|
||||
}
|
||||
.op-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #409eff;
|
||||
}
|
||||
.op-title { font-size: 15px; font-weight: 600; color: #303133; }
|
||||
</style>
|
||||
280
klp-ui/src/views/wms/coil/do/acid-split.vue
Normal file
280
klp-ui/src/views/wms/coil/do/acid-split.vue
Normal file
@@ -0,0 +1,280 @@
|
||||
<template>
|
||||
<div class="app-container acid-op-page">
|
||||
<el-row :gutter="16">
|
||||
|
||||
<!-- ── 左侧:分卷表单 ── -->
|
||||
<el-col :span="15">
|
||||
<div class="op-card">
|
||||
<div class="op-header">
|
||||
<span class="op-title">分卷操作</span>
|
||||
<el-tag size="mini" type="info" style="margin-left:8px">actionType = 520</el-tag>
|
||||
</div>
|
||||
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="110px" size="small">
|
||||
|
||||
<!-- 源卷 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="源卷号" prop="enterCoilNo">
|
||||
<el-input v-model="form.enterCoilNo" placeholder="回车自动查询 L2" clearable
|
||||
@keyup.enter.native="onEnterCoilInput"
|
||||
@blur="onEnterCoilInput" @clear="clearL2" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="原卷重量(t)">
|
||||
<el-input-number v-model="form.sourceWeight" :precision="3" :min="0"
|
||||
:controls="false" style="width:100%" placeholder="—" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left" style="margin:4px 0 12px">子卷明细</el-divider>
|
||||
|
||||
<!-- 子卷列表(动态) -->
|
||||
<el-row v-for="(child, idx) in form.children" :key="idx"
|
||||
:gutter="12" style="margin-bottom:8px" align="middle" type="flex">
|
||||
<el-col :span="1" style="text-align:center;color:#909399;font-size:12px">
|
||||
{{ idx + 1 }}
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-form-item
|
||||
:prop="'children.' + idx + '.coilNo'"
|
||||
:rules="[{ required: true, message: '请输入子卷号', trigger: 'blur' }]"
|
||||
style="margin-bottom:0"
|
||||
>
|
||||
<el-input v-model="child.coilNo" :placeholder="'子卷号 ' + (idx + 1)" clearable size="small" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<el-form-item style="margin-bottom:0">
|
||||
<el-input-number v-model="child.weight" :precision="3" :min="0"
|
||||
:controls="false" style="width:100%" placeholder="重量(t)" size="small" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item style="margin-bottom:0">
|
||||
<el-input-number v-model="child.length" :precision="1" :min="0"
|
||||
:controls="false" style="width:100%" placeholder="长度(m)" size="small" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="1">
|
||||
<el-button icon="el-icon-minus" size="mini" type="danger" circle
|
||||
:disabled="form.children.length <= 1"
|
||||
@click="removeChild(idx)" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div style="display:flex;align-items:center;margin-left:12px;margin-bottom:12px;gap:12px">
|
||||
<el-button size="mini" type="primary" plain icon="el-icon-plus" @click="addChild">
|
||||
添加子卷
|
||||
</el-button>
|
||||
<span v-if="weightLeft != null" :class="['weight-hint', weightLeft < 0 ? 'over' : '']">
|
||||
已分配 {{ weightAssigned.toFixed(3) }} t
|
||||
<template v-if="form.sourceWeight">
|
||||
/ 剩余 <b>{{ weightLeft.toFixed(3) }} t</b>
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出口厚度(mm)">
|
||||
<el-input-number v-model="form.exitThickness" :precision="3" :min="0"
|
||||
:controls="false" style="width:100%" placeholder="—" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出口宽度(mm)">
|
||||
<el-input-number v-model="form.exitWidth" :precision="1" :min="0"
|
||||
:controls="false" style="width:100%" placeholder="—" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="班组">
|
||||
<el-select v-model="form.team" placeholder="请选择" style="width:100%" clearable>
|
||||
<el-option label="甲班" value="甲" />
|
||||
<el-option label="乙班" value="乙" />
|
||||
<el-option label="丙班" value="丙" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="form.remark" placeholder="选填" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<div style="text-align:right;padding-top:4px">
|
||||
<el-button @click="resetForm">重置</el-button>
|
||||
<el-button type="primary" :loading="submitting" @click="submitForm">确认分卷</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 历史记录 -->
|
||||
<div class="op-card" style="margin-top:12px">
|
||||
<div class="op-header">
|
||||
<span class="op-title">最近分卷记录</span>
|
||||
<el-button size="mini" icon="el-icon-refresh" style="margin-left:auto" @click="loadHistory">刷新</el-button>
|
||||
</div>
|
||||
<el-table v-loading="historyLoading" :data="historyList" size="mini" border style="width:100%">
|
||||
<el-table-column prop="enterCoilNo" label="源卷号" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="currentCoilNo" label="子卷号" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column prop="exitThickness" label="出口厚(mm)" width="90" align="right" />
|
||||
<el-table-column prop="exitWidth" label="出口宽(mm)" width="90" align="right" />
|
||||
<el-table-column prop="netWeight" label="重量(t)" width="80" align="right" />
|
||||
<el-table-column prop="team" label="班组" width="60" align="center" />
|
||||
<el-table-column prop="createTime" label="录入时间" width="150" />
|
||||
</el-table>
|
||||
<pagination v-show="historyTotal > 0" :total="historyTotal"
|
||||
:page.sync="historyQuery.pageNum" :limit.sync="historyQuery.pageSize"
|
||||
@pagination="loadHistory" style="margin-top:6px" />
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- ── 右侧:L2 匹配面板 ── -->
|
||||
<el-col :span="9">
|
||||
<l2-match-panel :hot-coil-id="l2HotCoilId" @fill="applyL2Fill" />
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addCoilWarehouseOperationLog, listCoilWarehouseOperationLog } from '@/api/wms/coilWarehouseOperationLog'
|
||||
import L2MatchPanel from '../panels/L2MatchPanel.vue'
|
||||
|
||||
export default {
|
||||
name: 'AcidSplit',
|
||||
components: { L2MatchPanel },
|
||||
data() {
|
||||
return {
|
||||
form: this.defaultForm(),
|
||||
rules: {
|
||||
enterCoilNo: [{ required: true, message: '源卷号不能为空', trigger: 'blur' }],
|
||||
},
|
||||
submitting: false,
|
||||
l2HotCoilId: '',
|
||||
|
||||
historyLoading: false,
|
||||
historyList: [],
|
||||
historyTotal: 0,
|
||||
historyQuery: { pageNum: 1, pageSize: 10, actionType: 520 },
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
weightAssigned() {
|
||||
return this.form.children.reduce((s, c) => s + (c.weight || 0), 0)
|
||||
},
|
||||
weightLeft() {
|
||||
if (!this.form.sourceWeight) return null
|
||||
return this.form.sourceWeight - this.weightAssigned
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.loadHistory()
|
||||
},
|
||||
methods: {
|
||||
defaultForm() {
|
||||
return {
|
||||
enterCoilNo: '',
|
||||
sourceWeight: undefined,
|
||||
children: [{ coilNo: '', weight: undefined, length: undefined }],
|
||||
exitThickness: undefined,
|
||||
exitWidth: undefined,
|
||||
team: undefined,
|
||||
remark: '',
|
||||
actionType: 520,
|
||||
}
|
||||
},
|
||||
|
||||
addChild() {
|
||||
this.form.children.push({ coilNo: '', weight: undefined, length: undefined })
|
||||
},
|
||||
removeChild(idx) {
|
||||
this.form.children.splice(idx, 1)
|
||||
},
|
||||
|
||||
onEnterCoilInput() {
|
||||
this.l2HotCoilId = (this.form.enterCoilNo || '').trim()
|
||||
},
|
||||
clearL2() {
|
||||
this.l2HotCoilId = ''
|
||||
},
|
||||
|
||||
applyL2Fill(data) {
|
||||
if (data.exit_thick != null) this.form.exitThickness = parseFloat(data.exit_thick)
|
||||
if (data.exit_width != null) this.form.exitWidth = parseFloat(data.exit_width)
|
||||
if (data.entry_weight != null) this.form.sourceWeight = parseFloat(data.entry_weight)
|
||||
this.$message.success('L2 数据已写入表单')
|
||||
},
|
||||
|
||||
submitForm() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (!valid) return
|
||||
this.submitting = true
|
||||
// 每个子卷提交一条记录
|
||||
const records = this.form.children.map(child => ({
|
||||
enterCoilNo: this.form.enterCoilNo,
|
||||
currentCoilNo: child.coilNo,
|
||||
netWeight: child.weight,
|
||||
exitLength: child.length,
|
||||
exitThickness: this.form.exitThickness,
|
||||
exitWidth: this.form.exitWidth,
|
||||
team: this.form.team,
|
||||
remark: this.form.remark,
|
||||
actionType: 520,
|
||||
}))
|
||||
// 串行提交(也可并行,视后端是否支持批量而定)
|
||||
const submitAll = records.reduce((p, rec) => {
|
||||
return p.then(() => addCoilWarehouseOperationLog(rec))
|
||||
}, Promise.resolve())
|
||||
submitAll.then(() => {
|
||||
this.$modal.msgSuccess(`分卷操作已记录(共 ${records.length} 条子卷)`)
|
||||
this.resetForm()
|
||||
this.loadHistory()
|
||||
}).finally(() => { this.submitting = false })
|
||||
})
|
||||
},
|
||||
|
||||
resetForm() {
|
||||
this.form = this.defaultForm()
|
||||
this.l2HotCoilId = ''
|
||||
this.$nextTick(() => { this.$refs.form && this.$refs.form.clearValidate() })
|
||||
},
|
||||
|
||||
loadHistory() {
|
||||
this.historyLoading = true
|
||||
listCoilWarehouseOperationLog(this.historyQuery).then(res => {
|
||||
this.historyList = res.rows || []
|
||||
this.historyTotal = res.total || 0
|
||||
}).finally(() => { this.historyLoading = false })
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.acid-op-page { background: #f5f7fa; }
|
||||
.op-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
padding: 16px;
|
||||
}
|
||||
.op-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 2px solid #e6a23c;
|
||||
}
|
||||
.op-title { font-size: 15px; font-weight: 600; color: #303133; }
|
||||
|
||||
.weight-hint { font-size: 12px; color: #606266; }
|
||||
.weight-hint.over { color: #f56c6c; }
|
||||
.weight-hint b { color: #67c23a; }
|
||||
.weight-hint.over b { color: #f56c6c; }
|
||||
</style>
|
||||
@@ -1,123 +1,53 @@
|
||||
<template>
|
||||
<div class="trace-result-container">
|
||||
<!-- 操作步骤(基于标准化step渲染) -->
|
||||
<el-card shadow="hover" class="mb20" v-if="standardSteps && standardSteps.length > 0">
|
||||
<el-card shadow="hover" class="mb20" v-if="tracePanels && tracePanels.length > 0">
|
||||
<div slot="header" class="card-header">
|
||||
<span class="title-dot"></span>
|
||||
<span class="title-text">钢卷追溯操作步骤</span>
|
||||
</div>
|
||||
<el-timeline v-loading="loadingCoilDetails">
|
||||
<el-timeline-item v-for="(step, index) in standardSteps" :key="index"
|
||||
:timestamp="`步骤 ${step.original.display_step || step.original.step}`" placement="top">
|
||||
<el-card shadow="hover">
|
||||
<div class="step-header">
|
||||
<span class="step-action">{{ step.action }}</span>
|
||||
<el-tag size="mini" :type="getStepTagType(step.action)">{{ step.original.operation || step.action
|
||||
}}</el-tag>
|
||||
<el-tag size="mini" type="info" v-if="step.operation">
|
||||
操作人:{{ step.operation }}
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<!-- 标准化步骤详情 - 核心展示旧钢卷/新钢卷关键信息 -->
|
||||
<div class="step-details">
|
||||
<!-- 核心操作信息 -->
|
||||
<el-row class="detail-row" v-if="step.time">
|
||||
<el-col :span="8" class="detail-label">操作时间:</el-col>
|
||||
<el-col :span="16" class="detail-value">{{ step.time }}</el-col>
|
||||
</el-row>
|
||||
<template v-for="(panel, pIdx) in tracePanels">
|
||||
<!-- 线性段:单条时间线 -->
|
||||
<div v-if="panel.type === 'linear'" :key="'lin-' + pIdx" class="trace-panel-block">
|
||||
<el-timeline v-loading="loadingCoilDetails">
|
||||
<el-timeline-item v-for="(rawStep, idx) in panel.steps" :key="pIdx + '-' + idx"
|
||||
:timestamp="stepTimestamp(rawStep)" placement="top">
|
||||
<el-card shadow="hover">
|
||||
<trace-step-body :standard-step="formatStep(rawStep)"
|
||||
:get-step-tag-type="getStepTagType" :parse-changed-fields="parseChangedFields" />
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</div>
|
||||
|
||||
<!-- 新增步骤专属显示卷号 -->
|
||||
<el-row class="detail-row" v-if="step.action === '创建'">
|
||||
<el-col :span="8" class="detail-label">卷号:</el-col>
|
||||
<el-col :span="16" class="detail-value">{{ step.original.current_coil_no }}</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 旧钢卷关键信息(操作前)- 新增优化:展示核心字段,hover弹窗更多信息 -->
|
||||
<el-row class="detail-row" v-if="step.oldCoilInfoList && step.oldCoilInfoList.length">
|
||||
<el-col :span="8" class="detail-label">操作前钢卷:</el-col>
|
||||
<el-col :span="16" class="detail-value">
|
||||
<div class="coil-info-item" v-for="(coil, idx) in step.oldCoilInfoList" :key="idx">
|
||||
<el-popover placement="right" trigger="hover" width="400" v-if="coil.currentCoilNo != '-'"
|
||||
:loading="loadingCoilDetails">
|
||||
<div class="coil-detail-item">
|
||||
<p><b>入场卷号:</b>{{ coil.enterCoilNo || '-' }}</p>
|
||||
<p><b>当前卷号:</b>{{ coil.currentCoilNo || '-' }}</p>
|
||||
<p><b>物料类型:</b>{{ coil.materialType || '-' }}</p>
|
||||
<p><b>物料名称:</b>{{ coil.itemName || '-' }}</p>
|
||||
<p><b>规格:</b>{{ coil.specification || '-' }}</p>
|
||||
<p><b>材质:</b>{{ coil.material || '-' }}</p>
|
||||
<p><b>生产厂家:</b>{{ coil.manufacturer || '-' }}</p>
|
||||
<p><b>净重:</b>{{ coil.netWeight }} kg</p>
|
||||
<p><b>逻辑库区:</b>{{ coil.warehouseName || '-' }}</p>
|
||||
<p><b>实际库存:</b>{{ coil.actualWarehouseName || '-' }}</p>
|
||||
<p><b>镀层质量:</b>{{ coil.zincLayer || '-' }}</p>
|
||||
<p><b>质量状态:</b>{{ coil.qualityStatus || '-' }}</p>
|
||||
<p><b>创建时间:</b>{{ coil.createTime || '-' }}</p>
|
||||
</div>
|
||||
<div slot="reference" class="coil-info-summary">
|
||||
{{ coil.currentCoilNo || '-' }} {{ coil.itemName }}({{ coil.specification }})- {{
|
||||
coil.materialType }} - {{ coil.netWeight }}kg
|
||||
</div>
|
||||
</el-popover>
|
||||
|
||||
<div class="coil-info-summary coil-info-deleted" v-else>该钢卷已被回滚操作删除,无法查询到信息</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 新钢卷关键信息(操作后)- 新增优化:展示核心字段,hover弹窗更多信息 -->
|
||||
<el-row class="detail-row" v-if="step.newCoilInfoList && step.newCoilInfoList.length">
|
||||
<el-col :span="8" class="detail-label">操作后钢卷:</el-col>
|
||||
<el-col :span="16" class="detail-value">
|
||||
<div class="coil-info-item" v-for="(coil, idx) in step.newCoilInfoList" :key="idx">
|
||||
<el-popover placement="right" trigger="hover" width="400" v-if="coil.currentCoilNo != '-'"
|
||||
:loading="loadingCoilDetails">
|
||||
<div class="coil-detail-item">
|
||||
<p><b>入场卷号:</b>{{ coil.enterCoilNo || '-' }}</p>
|
||||
<p><b>当前卷号:</b>{{ coil.currentCoilNo || '-' }}</p>
|
||||
<p><b>物料类型:</b>{{ coil.materialType || '-' }}</p>
|
||||
<p><b>物料名称:</b>{{ coil.itemName || '-' }}</p>
|
||||
<p><b>规格:</b>{{ coil.specification || '-' }}</p>
|
||||
<p><b>材质:</b>{{ coil.material || '-' }}</p>
|
||||
<p><b>生产厂家:</b>{{ coil.manufacturer || '-' }}</p>
|
||||
<p><b>净重:</b>{{ coil.netWeight }} kg</p>
|
||||
<p><b>逻辑库区:</b>{{ coil.warehouseName || '-' }}</p>
|
||||
<p><b>实际库存:</b>{{ coil.actualWarehouseName || '-' }}</p>
|
||||
<p><b>镀层质量:</b>{{ coil.zincLayer || '-' }}</p>
|
||||
<p><b>质量状态:</b>{{ coil.qualityStatus || '-' }}</p>
|
||||
<p><b>创建时间:</b>{{ coil.createTime || '-' }}</p>
|
||||
</div>
|
||||
<div slot="reference" class="coil-info-summary">
|
||||
{{ coil.currentCoilNo || '-' }} {{ coil.itemName }}({{ coil.specification }})- {{
|
||||
coil.materialType }} - {{ coil.netWeight }}kg
|
||||
</div>
|
||||
</el-popover>
|
||||
|
||||
<div class="coil-info-summary coil-info-deleted" v-else>该钢卷已被回滚操作删除,无法查询到信息</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 更新专属:修改内容(保留原有逻辑,适配上下文) -->
|
||||
<el-row class="detail-row" v-if="step.changedFields">
|
||||
<el-col :span="8" class="detail-label">修改内容:</el-col>
|
||||
<el-col :span="16" class="detail-value">
|
||||
<div class="changed-field-item" v-for="(item, idx) in parseChangedFields(step.changedFields)"
|
||||
:key="idx">
|
||||
{{ item.field }}:<span class="old-value">{{ item.oldVal }}</span> → <span class="new-value">{{
|
||||
item.newVal }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<!-- 合卷段:多列并排 + 合卷汇聚 -->
|
||||
<div v-else-if="panel.type === 'merge_join'" :key="'mj-' + pIdx" class="trace-panel-block merge-join-block">
|
||||
<div class="merge-caption">合卷前各卷加工过程(并排展示)</div>
|
||||
<el-row :gutter="12" type="flex" class="merge-lanes-row">
|
||||
<el-col v-for="(lane, li) in panel.lanes" :key="li" :span="laneColSpan(panel.lanes.length)" class="merge-lane-col">
|
||||
<div class="lane-header-tag">
|
||||
{{ (panel.laneLabels && panel.laneLabels[li]) || ('来源 ' + (li + 1)) }}
|
||||
</div>
|
||||
<div class="lane-steps-stack">
|
||||
<el-card v-for="(rawStep, si) in lane" :key="si" shadow="hover" class="lane-step-card mb10">
|
||||
<trace-step-body :standard-step="formatStep(rawStep)"
|
||||
:get-step-tag-type="getStepTagType" :parse-changed-fields="parseChangedFields" compact />
|
||||
</el-card>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="merge-converge">
|
||||
<el-divider content-position="center">合卷汇聚</el-divider>
|
||||
<el-card shadow="hover">
|
||||
<trace-step-body :standard-step="formatStep(panel.mergeStep)"
|
||||
:get-step-tag-type="getStepTagType" :parse-changed-fields="parseChangedFields" />
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-card>
|
||||
|
||||
<!-- 无记录提示 -->
|
||||
<div class="empty-tip" v-if="!standardSteps || standardSteps.length === 0">
|
||||
<div class="empty-tip" v-if="!tracePanels || tracePanels.length === 0">
|
||||
<el-empty description="未找到相关钢卷记录"></el-empty>
|
||||
</div>
|
||||
</div>
|
||||
@@ -125,9 +55,11 @@
|
||||
|
||||
<script>
|
||||
import { listMaterialCoil } from '@/api/wms/coil';
|
||||
import TraceStepBody from './TraceStepBody.vue';
|
||||
|
||||
export default {
|
||||
name: 'CoilTraceResult',
|
||||
components: { TraceStepBody },
|
||||
props: {
|
||||
// 追溯结果数据
|
||||
traceResult: {
|
||||
@@ -144,21 +76,28 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 生成标准化步骤列表(核心:基于原始steps转换,新增旧/新钢卷信息列表)
|
||||
// 生成标准化步骤列表(用于批量拉取钢卷详情等)
|
||||
standardSteps() {
|
||||
if (!this.traceResult || !this.traceResult.steps || this.traceResult.steps.length === 0) {
|
||||
return [];
|
||||
}
|
||||
// 遍历原始步骤,转换为标准化步骤(包含旧/新钢卷关键信息)
|
||||
return this.traceResult.steps.map(step => this.formatStep(step));
|
||||
}
|
||||
},
|
||||
/** 后端拆好的线性段 / 合卷并排段;无 traceLayout 时退化为单条线性 */
|
||||
tracePanels() {
|
||||
if (this.traceResult && Array.isArray(this.traceResult.traceLayout) && this.traceResult.traceLayout.length) {
|
||||
return this.traceResult.traceLayout;
|
||||
}
|
||||
if (this.traceResult && this.traceResult.steps && this.traceResult.steps.length) {
|
||||
return [{ type: 'linear', steps: this.traceResult.steps }];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
traceResult: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
console.log('追溯结果更新,已生成标准化步骤:', this.standardSteps);
|
||||
// 步骤更新后,防抖获取钢卷详情(避免频繁请求)
|
||||
this.debounceFetchCoilDetails();
|
||||
} else {
|
||||
// 无追溯结果时,清空缓存
|
||||
@@ -169,6 +108,14 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
stepTimestamp(rawStep) {
|
||||
const d = rawStep.display_step != null ? rawStep.display_step : rawStep.step;
|
||||
return `步骤 ${d}`;
|
||||
},
|
||||
laneColSpan(laneCount) {
|
||||
const n = Math.max(1, laneCount || 1);
|
||||
return Math.max(1, Math.floor(24 / n));
|
||||
},
|
||||
// 根据操作类型获取标签样式
|
||||
getStepTagType(action) {
|
||||
const typeMap = {
|
||||
@@ -295,7 +242,6 @@ export default {
|
||||
standardStep.newCoilIds = originalStep.current_coil_id ? [originalStep.current_coil_id.trim()] : [];
|
||||
break;
|
||||
case '更新':
|
||||
console.log('更新操作后钢卷ID:', originalStep.new_coil_id, originalStep);
|
||||
standardStep.newCoilIds = originalStep.new_coil_id ? [originalStep.new_coil_id.trim()] : [];
|
||||
break;
|
||||
case '分卷':
|
||||
@@ -417,7 +363,6 @@ export default {
|
||||
step.restoredCoilInfo = step.newCoilInfoList[0] || null;
|
||||
}
|
||||
});
|
||||
console.log('更新后的标准化步骤:', this.standardSteps);
|
||||
} else {
|
||||
this.$message.warning('获取钢卷详情失败');
|
||||
}
|
||||
@@ -466,6 +411,59 @@ export default {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.trace-panel-block {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.trace-panel-block:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.merge-join-block {
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
background: #fafbfc;
|
||||
}
|
||||
|
||||
.merge-caption {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
margin-bottom: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.merge-lanes-row {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.merge-lane-col {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.lane-header-tag {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
margin-bottom: 8px;
|
||||
padding: 6px 10px;
|
||||
background: #ecf5ff;
|
||||
border-radius: 4px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.lane-steps-stack {
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.lane-step-card.mb10 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.merge-converge {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
/* 操作步骤样式增强 */
|
||||
.step-header {
|
||||
display: flex;
|
||||
|
||||
188
klp-ui/src/views/wms/coil/panels/L2MatchPanel.vue
Normal file
188
klp-ui/src/views/wms/coil/panels/L2MatchPanel.vue
Normal file
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<div class="l2-panel">
|
||||
|
||||
<!-- 最佳匹配 -->
|
||||
<div class="panel-block">
|
||||
<div class="pb-header">
|
||||
<span class="pb-title">最佳匹配</span>
|
||||
<el-tag v-if="bestMatch" size="mini" type="success" style="margin-left:6px">热卷号已找到</el-tag>
|
||||
<el-tag v-else-if="matchLoading" size="mini" type="info" style="margin-left:6px">查询中</el-tag>
|
||||
<el-tag v-else-if="hotCoilId" size="mini" type="warning" style="margin-left:6px">未找到</el-tag>
|
||||
<el-tag v-else size="mini" style="margin-left:6px">待输入</el-tag>
|
||||
</div>
|
||||
|
||||
<template v-if="bestMatch">
|
||||
<div class="match-grid">
|
||||
<div class="mg-item" v-for="f in PLAN_FIELDS" :key="f.key">
|
||||
<span class="mg-label">{{ f.label }}</span>
|
||||
<span class="mg-value">{{ formatPlanField(f.key, bestMatch[f.key]) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<el-button type="primary" size="small" style="width:100%;margin-top:10px"
|
||||
icon="el-icon-download" @click="$emit('fill', bestMatch)">
|
||||
写入表单
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<div v-else-if="matchLoading" class="pb-empty"><i class="el-icon-loading" /></div>
|
||||
<div v-else class="pb-empty">{{ hotCoilId ? '未找到匹配的 L2 计划记录' : '输入入场钢卷号后自动查询' }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 最近20条 -->
|
||||
<div class="panel-block" style="margin-top:12px">
|
||||
<div class="pb-header">
|
||||
<span class="pb-title">最近 20 条生产记录</span>
|
||||
<el-button type="text" size="mini" icon="el-icon-refresh"
|
||||
style="margin-left:auto" :loading="recentLoading" @click="loadRecentList">刷新</el-button>
|
||||
</div>
|
||||
|
||||
<el-table :data="recentList" size="mini" v-loading="recentLoading"
|
||||
max-height="400" border style="width:100%">
|
||||
<el-table-column label="热卷号" min-width="110" show-overflow-tooltip>
|
||||
<template slot-scope="{ row }">
|
||||
<span :class="(row.hot_coilid || row.encoilid) === hotCoilId ? 'hot-match' : ''">
|
||||
{{ row.hot_coilid || row.encoilid || '—' }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="出口厚" width="72" align="right">
|
||||
<template slot-scope="{ row }">{{ row.exit_thick != null ? row.exit_thick : '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="出口宽" width="72" align="right">
|
||||
<template slot-scope="{ row }">{{ row.exit_width != null ? row.exit_width : '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="长度(m)" width="76" align="right">
|
||||
<template slot-scope="{ row }">{{ row.exit_length != null ? row.exit_length : '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="重量(t)" width="72" align="right">
|
||||
<template slot-scope="{ row }">{{ row.entry_weight != null ? row.entry_weight : '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="" width="52" fixed="right" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-button type="text" size="mini" @click="$emit('fill', row)">写入</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getTimingPlanDetailByHotcoilId, getExcoilList } from '@/api/l2/timing'
|
||||
|
||||
const PLAN_FIELDS = [
|
||||
{ key: 'hot_coilid', label: '热卷号' },
|
||||
{ key: 'status', label: '状态' },
|
||||
{ key: 'process_code', label: '工艺编码' },
|
||||
{ key: 'entry_thick', label: '入口厚(mm)' },
|
||||
{ key: 'entry_width', label: '入口宽(mm)' },
|
||||
{ key: 'entry_weight', label: '入口重量(t)' },
|
||||
{ key: 'exit_thick', label: '出口厚(mm)' },
|
||||
{ key: 'exit_width', label: '出口宽(mm)' },
|
||||
{ key: 'exit_length', label: '出口长度(m)' },
|
||||
{ key: 'park_type', label: '包装要求' },
|
||||
{ key: 'trimming', label: '切边要求' },
|
||||
]
|
||||
|
||||
export default {
|
||||
name: 'L2MatchPanel',
|
||||
props: {
|
||||
hotCoilId: { type: String, default: '' }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
PLAN_FIELDS,
|
||||
bestMatch: null,
|
||||
matchLoading: false,
|
||||
recentList: [],
|
||||
recentLoading: false,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
hotCoilId(val) {
|
||||
if (val) this.loadBestMatch(val)
|
||||
else this.bestMatch = null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadRecentList()
|
||||
if (this.hotCoilId) this.loadBestMatch(this.hotCoilId)
|
||||
},
|
||||
methods: {
|
||||
loadBestMatch(hotCoilId) {
|
||||
this.bestMatch = null
|
||||
this.matchLoading = true
|
||||
getTimingPlanDetailByHotcoilId(hotCoilId).then(res => {
|
||||
const fr = res?.data?.firstRow
|
||||
this.bestMatch = (fr && Object.keys(fr).length > 0) ? fr : null
|
||||
}).catch(() => {}).finally(() => { this.matchLoading = false })
|
||||
},
|
||||
loadRecentList() {
|
||||
this.recentLoading = true
|
||||
getExcoilList(1, 20).then(res => {
|
||||
const rows = res?.data?.rows || []
|
||||
this.recentList = rows.map(row => this.normalizeExcoilRow(row))
|
||||
}).catch(() => {}).finally(() => { this.recentLoading = false })
|
||||
},
|
||||
formatPlanField(key, val) {
|
||||
if (val == null) return '—'
|
||||
if (key === 'trimming') {
|
||||
if (val === 1 || val === '1') return '净边料'
|
||||
if (val === 0 || val === '0') return '毛边料'
|
||||
}
|
||||
return val
|
||||
},
|
||||
normalizeExcoilRow(row) {
|
||||
const g = k => row[k] != null ? row[k] : (row[k.toUpperCase()] != null ? row[k.toUpperCase()] : null)
|
||||
return {
|
||||
hot_coilid: g('hot_coilid'),
|
||||
encoilid: g('encoilid'),
|
||||
excoilid: g('excoilid'),
|
||||
exit_thick: g('exit_thick'),
|
||||
exit_width: g('exit_width'),
|
||||
exit_length: g('exit_length'),
|
||||
entry_weight: g('used_entry_weight') != null ? g('used_entry_weight')
|
||||
: (g('meas_exit_weight') != null ? g('meas_exit_weight') : g('entry_weight')),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.l2-panel { font-size: 13px; }
|
||||
|
||||
.panel-block {
|
||||
background: #fff;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
}
|
||||
.pb-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #f0f2f5;
|
||||
}
|
||||
.pb-title { font-weight: 600; color: #303133; font-size: 13px; }
|
||||
|
||||
.match-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 6px 12px;
|
||||
}
|
||||
.mg-item { display: flex; flex-direction: column; }
|
||||
.mg-label { font-size: 11px; color: #909399; }
|
||||
.mg-value { font-size: 13px; color: #303133; font-weight: 500; }
|
||||
|
||||
.pb-empty {
|
||||
text-align: center;
|
||||
color: #c0c4cc;
|
||||
padding: 20px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.hot-match { color: #67c23a; font-weight: 600; }
|
||||
</style>
|
||||
196
klp-ui/src/views/wms/coil/panels/TraceStepBody.vue
Normal file
196
klp-ui/src/views/wms/coil/panels/TraceStepBody.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<div :class="compact ? 'step-details step-details-compact' : 'step-details'">
|
||||
<div class="step-header">
|
||||
<span class="step-action">{{ standardStep.action }}</span>
|
||||
<el-tag size="mini" :type="getStepTagType(standardStep.action)">
|
||||
{{ standardStep.original.operation || standardStep.action }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="info" v-if="standardStep.operation">
|
||||
操作人:{{ standardStep.operation }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<el-row class="detail-row" v-if="standardStep.time && !compact">
|
||||
<el-col :span="8" class="detail-label">操作时间:</el-col>
|
||||
<el-col :span="16" class="detail-value">{{ standardStep.time }}</el-col>
|
||||
</el-row>
|
||||
<el-row class="detail-row" v-if="compact && standardStep.time">
|
||||
<el-col :span="24" class="detail-value text-muted">{{ standardStep.time }}</el-col>
|
||||
</el-row>
|
||||
<el-row class="detail-row" v-if="standardStep.action === '创建'">
|
||||
<el-col :span="compact ? 24 : 8" class="detail-label">卷号:</el-col>
|
||||
<el-col :span="compact ? 24 : 16" class="detail-value">{{ standardStep.original.current_coil_no }}</el-col>
|
||||
</el-row>
|
||||
<el-row class="detail-row" v-if="standardStep.oldCoilInfoList && standardStep.oldCoilInfoList.length">
|
||||
<el-col :span="compact ? 24 : 8" class="detail-label">操作前钢卷:</el-col>
|
||||
<el-col :span="compact ? 24 : 16" class="detail-value">
|
||||
<div class="coil-info-item" v-for="(coil, idx) in standardStep.oldCoilInfoList" :key="idx">
|
||||
<el-popover placement="right" trigger="hover" width="400" v-if="coil.currentCoilNo != '-'">
|
||||
<div class="coil-detail-item">
|
||||
<p><b>入场卷号:</b>{{ coil.enterCoilNo || '-' }}</p>
|
||||
<p><b>当前卷号:</b>{{ coil.currentCoilNo || '-' }}</p>
|
||||
<p><b>物料类型:</b>{{ coil.materialType || '-' }}</p>
|
||||
<p><b>物料名称:</b>{{ coil.itemName || '-' }}</p>
|
||||
<p><b>规格:</b>{{ coil.specification || '-' }}</p>
|
||||
<p><b>材质:</b>{{ coil.material || '-' }}</p>
|
||||
<p><b>生产厂家:</b>{{ coil.manufacturer || '-' }}</p>
|
||||
<p><b>净重:</b>{{ coil.netWeight }} kg</p>
|
||||
<p><b>逻辑库区:</b>{{ coil.warehouseName || '-' }}</p>
|
||||
<p><b>实际库存:</b>{{ coil.actualWarehouseName || '-' }}</p>
|
||||
<p><b>镀层质量:</b>{{ coil.zincLayer || '-' }}</p>
|
||||
<p><b>质量状态:</b>{{ coil.qualityStatus || '-' }}</p>
|
||||
<p><b>创建时间:</b>{{ coil.createTime || '-' }}</p>
|
||||
</div>
|
||||
<div slot="reference" class="coil-info-summary">
|
||||
{{ coil.currentCoilNo || '-' }} {{ coil.itemName }}({{ coil.specification }})-{{ coil.materialType }}-{{ coil.netWeight }}kg
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="coil-info-summary coil-info-deleted" v-else>该钢卷已被回滚操作删除,无法查询到信息</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="detail-row" v-if="standardStep.newCoilInfoList && standardStep.newCoilInfoList.length">
|
||||
<el-col :span="compact ? 24 : 8" class="detail-label">操作后钢卷:</el-col>
|
||||
<el-col :span="compact ? 24 : 16" class="detail-value">
|
||||
<div class="coil-info-item" v-for="(coil, idx) in standardStep.newCoilInfoList" :key="idx">
|
||||
<el-popover placement="right" trigger="hover" width="400" v-if="coil.currentCoilNo != '-'">
|
||||
<div class="coil-detail-item">
|
||||
<p><b>入场卷号:</b>{{ coil.enterCoilNo || '-' }}</p>
|
||||
<p><b>当前卷号:</b>{{ coil.currentCoilNo || '-' }}</p>
|
||||
<p><b>物料类型:</b>{{ coil.materialType || '-' }}</p>
|
||||
<p><b>物料名称:</b>{{ coil.itemName || '-' }}</p>
|
||||
<p><b>规格:</b>{{ coil.specification || '-' }}</p>
|
||||
<p><b>材质:</b>{{ coil.material || '-' }}</p>
|
||||
<p><b>生产厂家:</b>{{ coil.manufacturer || '-' }}</p>
|
||||
<p><b>净重:</b>{{ coil.netWeight }} kg</p>
|
||||
<p><b>逻辑库区:</b>{{ coil.warehouseName || '-' }}</p>
|
||||
<p><b>实际库存:</b>{{ coil.actualWarehouseName || '-' }}</p>
|
||||
<p><b>镀层质量:</b>{{ coil.zincLayer || '-' }}</p>
|
||||
<p><b>质量状态:</b>{{ coil.qualityStatus || '-' }}</p>
|
||||
<p><b>创建时间:</b>{{ coil.createTime || '-' }}</p>
|
||||
</div>
|
||||
<div slot="reference" class="coil-info-summary">
|
||||
{{ coil.currentCoilNo || '-' }} {{ coil.itemName }}({{ coil.specification }})-{{ coil.materialType }}-{{ coil.netWeight }}kg
|
||||
</div>
|
||||
</el-popover>
|
||||
<div class="coil-info-summary coil-info-deleted" v-else>该钢卷已被回滚操作删除,无法查询到信息</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="detail-row" v-if="standardStep.changedFields">
|
||||
<el-col :span="compact ? 24 : 8" class="detail-label">修改内容:</el-col>
|
||||
<el-col :span="compact ? 24 : 16" class="detail-value">
|
||||
<div class="changed-field-item" v-for="(item, idx) in parseChangedFields(standardStep.changedFields)" :key="idx">
|
||||
{{ item.field }}:<span class="old-value">{{ item.oldVal }}</span> → <span class="new-value">{{ item.newVal }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TraceStepBody',
|
||||
props: {
|
||||
standardStep: { type: Object, required: true },
|
||||
getStepTagType: { type: Function, required: true },
|
||||
parseChangedFields: { type: Function, required: true },
|
||||
compact: { type: Boolean, default: false },
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.step-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.step-action {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.step-details {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.step-details-compact .step-header {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
margin-bottom: 12px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.changed-field-item {
|
||||
margin-bottom: 4px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.old-value {
|
||||
color: #F56C6C;
|
||||
}
|
||||
|
||||
.new-value {
|
||||
color: #67C23A;
|
||||
}
|
||||
|
||||
.coil-detail-item {
|
||||
font-size: 13px;
|
||||
line-height: 1.8;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.coil-detail-item p {
|
||||
margin: 0;
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.coil-detail-item p b {
|
||||
color: #666;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.coil-info-item {
|
||||
margin-bottom: 8px;
|
||||
display: inline-block;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.coil-info-summary {
|
||||
cursor: pointer;
|
||||
color: #409EFF;
|
||||
background: #f5faff;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #e6f7ff;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.coil-info-deleted {
|
||||
color: #F56C6C;
|
||||
background: #fff5f5;
|
||||
}
|
||||
</style>
|
||||
@@ -26,25 +26,9 @@
|
||||
|
||||
<!-- 主内容区 - 左右布局 -->
|
||||
<div class="content-wrapper">
|
||||
<!-- 左侧:酸轧信息推荐 -->
|
||||
<!-- 左侧:L2 匹配面板 -->
|
||||
<div>
|
||||
<!-- 酸连轧最近10条记录展示 -->
|
||||
<el-card class="recent-records-card" v-if="acidRecentRecords && acidRecentRecords.length > 0">
|
||||
<div slot="header" class="card-header">
|
||||
<span><i class="el-icon-time"></i> 酸连轧最近记录</span>
|
||||
</div>
|
||||
|
||||
<el-table :data="acidRecentRecords" stripe size="small" @row-click="handleClickRecord">
|
||||
<el-table-column prop="currentCoilNo" label="加工前卷号" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="excoilId" label="出口卷号" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="exitWeight" label="出口重量(t)" width="100">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.exitWeight ? scope.row.exitWeight + ' t' : '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="team" label="班组" width="80"></el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<l2-match-panel :hot-coil-id="l2HotCoilId" @fill="applyL2Fill" />
|
||||
</div>
|
||||
<!-- 右侧:更新表单 -->
|
||||
<div>
|
||||
@@ -138,35 +122,6 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="质量状态" prop="qualityStatus">
|
||||
<el-select v-model="updateForm.qualityStatus" placeholder="请选择质量状态" style="width: 100%">
|
||||
<el-option v-for="item in dict.type.coil_quality_status" :key="item.value" :label="item.label"
|
||||
:value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="切边要求" prop="trimmingRequirement">
|
||||
<el-select v-model="updateForm.trimmingRequirement" placeholder="请选择切边要求" style="width: 100%">
|
||||
<el-option label="净边料" value="净边料" />
|
||||
<el-option label="毛边料" value="毛边料" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="包装要求" prop="packagingRequirement">
|
||||
<el-select v-model="updateForm.packagingRequirement" placeholder="请选择包装要求" style="width: 100%">
|
||||
<el-option label="裸包" value="裸包" />
|
||||
<el-option label="普包" value="普包" />
|
||||
<el-option label="简包" value="简包" />
|
||||
<el-option label="精包" value="精包" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="毛重(t)" prop="grossWeight">
|
||||
@@ -361,8 +316,6 @@
|
||||
<script>
|
||||
import { getMaterialCoil, updateMaterialCoil, getFirstHeatCoilMaterial } from '@/api/wms/coil';
|
||||
import { completeAction, getPendingAction } from '@/api/wms/pendingAction';
|
||||
// import { getTimingPlanList } from '@/api/l2/timing'
|
||||
import { getAcidTypingPrefill } from '@/api/pocket/acidTyping';
|
||||
import { saveCoilCache, getCoilCacheByCoilId, delCoilCache } from '@/api/wms/coilCache';
|
||||
import ActualWarehouseSelect from "@/components/KLPService/ActualWarehouseSelect";
|
||||
import RawMaterialSelect from "@/components/KLPService/RawMaterialSelect";
|
||||
@@ -373,6 +326,7 @@ import AbnormalForm from './components/AbnormalForm';
|
||||
import { generateCoilNoPrefix } from "@/utils/coil/coilNo";
|
||||
import { addCoilContractRel } from "@/api/wms/coilContractRel";
|
||||
import ContractSelect from "@/components/KLPService/ContractSelect";
|
||||
import L2MatchPanel from './panels/L2MatchPanel.vue';
|
||||
|
||||
|
||||
export default {
|
||||
@@ -384,7 +338,8 @@ export default {
|
||||
WarehouseSelect,
|
||||
TimeInput,
|
||||
AbnormalForm,
|
||||
ContractSelect
|
||||
ContractSelect,
|
||||
L2MatchPanel,
|
||||
},
|
||||
dicts: ['coil_quality_status', 'coil_abnormal_position', 'coil_abnormal_code', 'coil_abnormal_degree', 'coil_business_purpose'],
|
||||
data() {
|
||||
@@ -479,9 +434,6 @@ export default {
|
||||
type: 'info',
|
||||
title: ''
|
||||
},
|
||||
isAcidRolling: false,
|
||||
// 酸连轧最近记录
|
||||
acidRecentRecords: [],
|
||||
// 异常信息
|
||||
abnormals: [],
|
||||
// 异常表单弹窗
|
||||
@@ -507,6 +459,9 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
l2HotCoilId() {
|
||||
return (this.currentInfo && this.currentInfo.enterCoilNo) || ''
|
||||
},
|
||||
// 动态显示标签
|
||||
getItemLabel() {
|
||||
if (this.updateForm.materialType === '成品') {
|
||||
@@ -530,98 +485,13 @@ export default {
|
||||
// 从路由参数获取coilId和actionId
|
||||
const coilId = this.$route.query.coilId;
|
||||
const actionId = this.$route.query.actionId;
|
||||
let actionType = ''
|
||||
const pendingActionRes = await getPendingAction(actionId)
|
||||
actionType = pendingActionRes.data.actionType
|
||||
|
||||
// 填写生产开始时间
|
||||
this.$set(this.updateForm, 'productionStartTime', pendingActionRes.data.createTime)
|
||||
|
||||
this.isAcidRolling = (actionType == 11)
|
||||
|
||||
if (this.isAcidRolling) {
|
||||
this.acidPrefill.visible = true
|
||||
this.acidPrefill.type = 'info'
|
||||
this.acidPrefill.title = '正在结合酸轧二级系统自动填写部分信息...'
|
||||
}
|
||||
|
||||
if (coilId) {
|
||||
await this.loadCoilInfo(coilId);
|
||||
|
||||
if (this.isAcidRolling) {
|
||||
const currentCoilNo = this.currentInfo && this.currentInfo.currentCoilNo
|
||||
|
||||
if (!currentCoilNo) {
|
||||
this.acidPrefill.type = 'warning'
|
||||
this.acidPrefill.title = '当前钢卷号为空'
|
||||
} else {
|
||||
try {
|
||||
const prefillRes = await getAcidTypingPrefill(currentCoilNo)
|
||||
const prefill = prefillRes && prefillRes.data
|
||||
|
||||
if (!prefill || prefill.length === 0) {
|
||||
this.acidPrefill.type = 'info'
|
||||
this.acidPrefill.title = '未在二级系统中查找到对应信息,请自行填写'
|
||||
} else {
|
||||
// 处理返回的列表数据
|
||||
if (Array.isArray(prefill)) {
|
||||
this.acidRecentRecords = prefill;
|
||||
|
||||
// 使用第一条记录填充表单(保持原有逻辑)
|
||||
const firstRecord = prefill[0];
|
||||
if (firstRecord) {
|
||||
if (firstRecord.exitWeight != null && firstRecord.exitWeight !== '') {
|
||||
const w = Number(firstRecord.exitWeight)
|
||||
if (!Number.isNaN(w)) {
|
||||
this.$set(this.updateForm, 'grossWeight', w)
|
||||
this.$set(this.updateForm, 'netWeight', w)
|
||||
}
|
||||
}
|
||||
|
||||
if (firstRecord.exitLength != null && firstRecord.exitLength !== '') {
|
||||
const len = Number(firstRecord.exitLength)
|
||||
if (!Number.isNaN(len)) {
|
||||
this.$set(this.updateForm, 'length', len)
|
||||
}
|
||||
}
|
||||
|
||||
if (firstRecord.team) {
|
||||
this.$set(this.updateForm, 'team', firstRecord.team)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 为了兼容旧版本的单个对象返回格式
|
||||
if (prefill.exitWeight != null && prefill.exitWeight !== '') {
|
||||
const w = Number(prefill.exitWeight)
|
||||
if (!Number.isNaN(w)) {
|
||||
this.$set(this.updateForm, 'grossWeight', w)
|
||||
this.$set(this.updateForm, 'netWeight', w)
|
||||
}
|
||||
}
|
||||
|
||||
if (prefill.exitLength != null && prefill.exitLength !== '') {
|
||||
const len = Number(prefill.exitLength)
|
||||
if (!Number.isNaN(len)) {
|
||||
this.$set(this.updateForm, 'length', len)
|
||||
}
|
||||
}
|
||||
|
||||
if (prefill.team) {
|
||||
this.$set(this.updateForm, 'team', prefill.team)
|
||||
}
|
||||
}
|
||||
|
||||
this.acidPrefill.type = 'success'
|
||||
this.acidPrefill.title = '已结合酸轧二级系统完成部分信息填写'
|
||||
console.log('[typing] acid rolling prefill applied:', prefill)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('[typing] acid rolling prefill request failed:', e)
|
||||
this.acidPrefill.type = 'error'
|
||||
this.acidPrefill.title = '未在二级系统中查找到对应信息,故自动填写失败'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const currentCoilNoPrefix = generateCoilNoPrefix()
|
||||
@@ -653,6 +523,36 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
applyL2Fill(data) {
|
||||
if (data.entry_weight != null) {
|
||||
const w = parseFloat(data.entry_weight)
|
||||
if (!Number.isNaN(w)) {
|
||||
this.$set(this.updateForm, 'grossWeight', w)
|
||||
this.$set(this.updateForm, 'netWeight', w)
|
||||
}
|
||||
}
|
||||
if (data.exit_length != null) {
|
||||
const len = parseFloat(data.exit_length)
|
||||
if (!Number.isNaN(len)) {
|
||||
this.$set(this.updateForm, 'length', len)
|
||||
this.$set(this.updateForm, 'actualLength', len)
|
||||
}
|
||||
}
|
||||
if (data.exit_thick != null) this.$set(this.updateForm, 'actualThickness', parseFloat(data.exit_thick))
|
||||
if (data.exit_width != null) this.$set(this.updateForm, 'actualWidth', parseFloat(data.exit_width))
|
||||
// 包装要求
|
||||
if (data.park_type != null && data.park_type !== '') {
|
||||
this.$set(this.updateForm, 'packagingRequirement', data.park_type)
|
||||
}
|
||||
// 切边要求:1=净边料,0=毛边料
|
||||
if (data.trimming != null) {
|
||||
const t = String(data.trimming)
|
||||
if (t === '1') this.$set(this.updateForm, 'trimmingRequirement', '净边料')
|
||||
else if (t === '0') this.$set(this.updateForm, 'trimmingRequirement', '毛边料')
|
||||
}
|
||||
this.$message.success('L2 数据已写入表单')
|
||||
},
|
||||
|
||||
// 处理材料类型变化
|
||||
handleMaterialTypeChange(value) {
|
||||
// 清空物品选择
|
||||
@@ -667,17 +567,6 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
handleClickRecord(row) {
|
||||
this.updateForm = {
|
||||
...this.updateForm,
|
||||
currentCoilNo: row.excoilId,
|
||||
team: row.team,
|
||||
netWeight: row.exitWeight,
|
||||
grossWeight: row.exitWeight,
|
||||
length: row.exitLength,
|
||||
}
|
||||
},
|
||||
|
||||
// 加载钢卷信息
|
||||
async loadCoilInfo(coilId) {
|
||||
try {
|
||||
@@ -1050,7 +939,7 @@ export default {
|
||||
.content-wrapper {
|
||||
display: grid;
|
||||
margin-top: 10px;
|
||||
grid-template-columns: 600px 1fr;
|
||||
grid-template-columns: 340px 1fr;
|
||||
gap: 10px;
|
||||
align-items: stretch; // 改为stretch,让子元素高度一致
|
||||
}
|
||||
|
||||
@@ -178,75 +178,78 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 参数对比表 -->
|
||||
<div v-if="!detailLoading && detailParams.length === 0" class="detail-empty">
|
||||
暂无参数数据
|
||||
</div>
|
||||
<template v-else>
|
||||
<div
|
||||
v-for="group in detailParamGroups"
|
||||
:key="group.planId"
|
||||
class="param-group"
|
||||
>
|
||||
<div class="param-group-hd">
|
||||
<span class="pg-segment">{{ group.segmentName || group.segmentType || '—' }}</span>
|
||||
<span class="pg-point">{{ group.pointName }}
|
||||
<span v-if="group.pointCode" class="pg-code">{{ group.pointCode }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<el-table
|
||||
:data="group.params"
|
||||
size="mini"
|
||||
border
|
||||
:row-class-name="paramRowClass"
|
||||
<!-- 参数分段 Tab -->
|
||||
<div class="seg-tabs">
|
||||
<div class="seg-tab-bar">
|
||||
<button
|
||||
v-for="seg in detailSegmentTabs"
|
||||
:key="seg.segmentType"
|
||||
:class="['seg-tab-btn', { 'is-active': detailActiveTab === seg.segmentType }]"
|
||||
@click="detailActiveTab = seg.segmentType"
|
||||
>
|
||||
<el-table-column label="参数名称" prop="paramName" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="编码" prop="paramCode" width="90" show-overflow-tooltip />
|
||||
<el-table-column label="单位" prop="unit" width="56" align="center">
|
||||
<template slot-scope="{ row }">{{ row.unit || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设定目标" width="84" align="right">
|
||||
<template slot-scope="{ row }">
|
||||
<span :class="row._hasAnomaly ? 'val-anomaly' : ''">{{ fmtNum(row.targetValue) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下限" width="72" align="right">
|
||||
<template slot-scope="{ row }">{{ fmtNum(row.lowerLimit) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上限" width="72" align="right">
|
||||
<template slot-scope="{ row }">{{ fmtNum(row.upperLimit) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际最大" width="84" align="right">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-if="row._hasAnomaly" :class="row._anomalyType === 'UPPER' ? 'val-danger' : 'val-ok'">{{ fmtNum(row._actualMax) }}</span>
|
||||
<span v-else class="dim">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际最小" width="84" align="right">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-if="row._hasAnomaly" :class="row._anomalyType === 'LOWER' ? 'val-danger' : 'val-ok'">{{ fmtNum(row._actualMin) }}</span>
|
||||
<span v-else class="dim">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="偏差" width="72" align="right">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-if="row._hasAnomaly" class="val-danger">
|
||||
{{ fmtNum(row._anomalyType === 'UPPER' ? row._deviationMax : row._deviationMin) }}
|
||||
</span>
|
||||
<span v-else class="dim">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="72" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag v-if="row._hasAnomaly" type="danger" size="mini" effect="plain">
|
||||
{{ row._anomalyType === 'UPPER' ? '超上限' : row._anomalyType === 'LOWER' ? '低于下限' : '异常' }}
|
||||
</el-tag>
|
||||
<el-tag v-else type="success" size="mini" effect="plain">正常</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
{{ seg.segmentLabel }}
|
||||
<span v-if="seg.anomalyCount > 0" class="seg-badge">{{ seg.anomalyCount }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-for="seg in detailSegmentTabs">
|
||||
<div v-show="detailActiveTab === seg.segmentType" :key="seg.segmentType" class="seg-tab-pane">
|
||||
<div v-if="!detailLoading && seg.groups.length === 0" class="detail-empty">暂无参数数据</div>
|
||||
<div v-for="group in seg.groups" :key="group.planId" class="param-group">
|
||||
<div class="param-group-hd">
|
||||
<span class="pg-point">{{ group.pointName }}</span>
|
||||
<span v-if="group.pointCode" class="pg-code">{{ group.pointCode }}</span>
|
||||
</div>
|
||||
<el-table :data="group.params" size="mini" border :row-class-name="paramRowClass">
|
||||
<el-table-column label="参数名称" prop="paramName" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="编码" prop="paramCode" width="100" show-overflow-tooltip />
|
||||
<el-table-column label="单位" prop="unit" width="68" align="center">
|
||||
<template slot-scope="{ row }">{{ row.unit || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="设定目标" width="84" align="right">
|
||||
<template slot-scope="{ row }">
|
||||
<span :class="row._hasAnomaly ? 'val-anomaly' : ''">{{ fmtNum(row.targetValue) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下限" width="72" align="right">
|
||||
<template slot-scope="{ row }">{{ fmtNum(row.lowerLimit) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="上限" width="72" align="right">
|
||||
<template slot-scope="{ row }">{{ fmtNum(row.upperLimit) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际最大" width="84" align="right">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-if="row._hasAnomaly" :class="row._anomalyType === 'UPPER' ? 'val-danger' : 'val-ok'">{{ fmtNum(row._actualMax) }}</span>
|
||||
<span v-else class="dim">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="实际最小" width="84" align="right">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-if="row._hasAnomaly" :class="row._anomalyType === 'LOWER' ? 'val-danger' : 'val-ok'">{{ fmtNum(row._actualMin) }}</span>
|
||||
<span v-else class="dim">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="偏差" width="72" align="right">
|
||||
<template slot-scope="{ row }">
|
||||
<span v-if="row._hasAnomaly" class="val-danger">
|
||||
{{ fmtNum(row._anomalyType === 'UPPER' ? row._deviationMax : row._deviationMin) }}
|
||||
</span>
|
||||
<span v-else class="dim">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="76" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag v-if="row._hasAnomaly" type="danger" size="mini" effect="plain">
|
||||
{{ row._anomalyType === 'UPPER' ? '超上限' : row._anomalyType === 'LOWER' ? '低于下限' : '异常' }}
|
||||
</el-tag>
|
||||
<el-tag v-else type="success" size="mini" effect="plain">正常</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
@@ -391,9 +394,41 @@
|
||||
import { listProcessCoilRecord, batchRebindCoilRecord } from '@/api/wms/processCoilRecord'
|
||||
import { listProcessSpec } from '@/api/wms/processSpec'
|
||||
import { listProcessSpecVersion } from '@/api/wms/processSpecVersion'
|
||||
import { listProcessPlan } from '@/api/wms/processPlan'
|
||||
import { listProcessPlanParam } from '@/api/wms/processPlanParam'
|
||||
import { listProcessPlan, addProcessPlan } from '@/api/wms/processPlan'
|
||||
import { listProcessPlanParam, addProcessPlanParam } from '@/api/wms/processPlanParam'
|
||||
import { listAllProcessAnomaly } from '@/api/wms/processAnomaly'
|
||||
import { getPresetSetupByCoilId } from '@/api/l2/timing'
|
||||
|
||||
// 三段 L1 预设设定值的结构定义(对应 PLTCM_PRESET_SETUP 表)
|
||||
const PRESET_PLANS = [
|
||||
{
|
||||
segmentType: 'INLET', segmentName: '入口段', pointName: '入口设定值', pointCode: 'PRESET_INLET', sortOrder: 10,
|
||||
params: [
|
||||
{ paramCode: 'POR_TEN', paramName: '开卷机单位张力', unit: 'N/mm²' },
|
||||
{ paramCode: 'CEL_TEN', paramName: '入口活套单位张力', unit: 'N/mm²' },
|
||||
{ paramCode: 'FLAT_MESH_1', paramName: '矫直机1#辊插入量', unit: 'mm' },
|
||||
{ paramCode: 'FLAT_MESH_2', paramName: '矫直机2#辊插入量', unit: 'mm' }
|
||||
]
|
||||
},
|
||||
{
|
||||
segmentType: 'PROCESS', segmentName: '工艺段', pointName: '工艺设定值', pointCode: 'PRESET_PROCESS', sortOrder: 20,
|
||||
params: [
|
||||
{ paramCode: 'TLV_TEN', paramName: '拉弯矫直机单位张力', unit: 'N/mm²' },
|
||||
{ paramCode: 'TLV_ELONG', paramName: '拉弯矫直机延伸率', unit: '%' },
|
||||
{ paramCode: 'TLV_MESH_1', paramName: '弯曲辊1#弯辊量', unit: 'mm' },
|
||||
{ paramCode: 'TLV_MESH_2', paramName: '弯曲辊2#弯辊量', unit: 'mm' }
|
||||
]
|
||||
},
|
||||
{
|
||||
segmentType: 'OUTLET', segmentName: '出口段', pointName: '出口设定值', pointCode: 'PRESET_OUTLET', sortOrder: 30,
|
||||
params: [
|
||||
{ paramCode: 'TR_TEN', paramName: '酸洗出口张力辊张力', unit: 'N/mm²' },
|
||||
{ paramCode: 'TRIM_TEN', paramName: '切边段单位张力', unit: 'N/mm²' },
|
||||
{ paramCode: 'TEL_TEN', paramName: '联机活套单位张力', unit: 'N/mm²' },
|
||||
{ paramCode: 'CXL_TEN', paramName: '出口活套单位张力', unit: 'N/mm²' }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
const VERSION_STATUS = [
|
||||
{ value: 'DRAFT', label: '草稿' },
|
||||
@@ -422,6 +457,7 @@ export default {
|
||||
detailLoading: false,
|
||||
detailRow: null,
|
||||
detailParams: [],
|
||||
detailActiveTab: 'INLET',
|
||||
|
||||
bindDialogVisible: false,
|
||||
removeOldRecord: false,
|
||||
@@ -456,6 +492,23 @@ export default {
|
||||
}
|
||||
return Object.values(planMap).sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))
|
||||
},
|
||||
detailSegmentTabs() {
|
||||
const SEG_ORDER = ['INLET', 'PROCESS', 'OUTLET']
|
||||
const SEG_LABELS = { INLET: '入口段', PROCESS: '工艺段', OUTLET: '出口段' }
|
||||
const map = {}
|
||||
for (const g of this.detailParamGroups) {
|
||||
const seg = g.segmentType || 'OTHER'
|
||||
if (!map[seg]) {
|
||||
map[seg] = { segmentType: seg, segmentLabel: SEG_LABELS[seg] || seg, anomalyCount: 0, groups: [] }
|
||||
}
|
||||
map[seg].groups.push(g)
|
||||
map[seg].anomalyCount += g.params.filter(p => p._hasAnomaly).length
|
||||
}
|
||||
for (const seg of SEG_ORDER) {
|
||||
if (!map[seg]) map[seg] = { segmentType: seg, segmentLabel: SEG_LABELS[seg], anomalyCount: 0, groups: [] }
|
||||
}
|
||||
return SEG_ORDER.map(s => map[s])
|
||||
},
|
||||
// 选中行是否全来自同一个版本(决定是否可以"移除旧记录")
|
||||
singleSourceVersion() {
|
||||
if (!this.selected.length) return false
|
||||
@@ -580,6 +633,7 @@ export default {
|
||||
openDetail(row) {
|
||||
this.detailRow = row
|
||||
this.detailParams = []
|
||||
this.detailActiveTab = 'INLET'
|
||||
this.detailDrawerVisible = true
|
||||
this.loadDetailData(row)
|
||||
},
|
||||
@@ -587,6 +641,45 @@ export default {
|
||||
this.detailRow = null
|
||||
this.detailParams = []
|
||||
},
|
||||
// Returns true if any preset plan points were auto-created
|
||||
async autoInitPresetParams(row, existingPlans) {
|
||||
try {
|
||||
const existingCodes = new Set(existingPlans.map(p => p.pointCode))
|
||||
const missing = PRESET_PLANS.filter(p => !existingCodes.has(p.pointCode))
|
||||
if (!missing.length) return false
|
||||
const srcId = row.enCoilId || row.coilId
|
||||
if (!srcId) return false
|
||||
const presetRes = await getPresetSetupByCoilId(srcId)
|
||||
const preset = (presetRes && presetRes.data && presetRes.data.data) || {}
|
||||
for (const def of missing) {
|
||||
const planRes = await addProcessPlan({
|
||||
versionId: row.versionId,
|
||||
segmentType: def.segmentType,
|
||||
segmentName: def.segmentName,
|
||||
pointName: def.pointName,
|
||||
pointCode: def.pointCode,
|
||||
sortOrder: def.sortOrder
|
||||
})
|
||||
const planId = planRes.data
|
||||
for (const p of def.params) {
|
||||
const val = preset[p.paramCode.toLowerCase()] ?? preset[p.paramCode]
|
||||
await addProcessPlanParam({
|
||||
planId,
|
||||
paramCode: p.paramCode,
|
||||
paramName: p.paramName,
|
||||
unit: p.unit,
|
||||
targetValue: (val !== null && val !== undefined && val !== '') ? Number(val) : null,
|
||||
presetSrcId: srcId
|
||||
})
|
||||
}
|
||||
}
|
||||
return true
|
||||
} catch (e) {
|
||||
console.error('[autoInitPresetParams] failed', e)
|
||||
return false
|
||||
}
|
||||
},
|
||||
|
||||
async loadDetailData(row) {
|
||||
if (!row.versionId) return
|
||||
this.detailLoading = true
|
||||
@@ -595,7 +688,14 @@ export default {
|
||||
listProcessPlan({ versionId: row.versionId, pageNum: 1, pageSize: 200 }),
|
||||
listAllProcessAnomaly({ coilId: row.coilId, versionId: row.versionId })
|
||||
])
|
||||
const plans = plansRes.rows || []
|
||||
let plans = plansRes.rows || []
|
||||
|
||||
const added = await this.autoInitPresetParams(row, plans)
|
||||
if (added) {
|
||||
const refreshed = await listProcessPlan({ versionId: row.versionId, pageNum: 1, pageSize: 200 })
|
||||
plans = refreshed.rows || []
|
||||
}
|
||||
|
||||
const anomalyList = anomalyRes.data || []
|
||||
const anomalyMap = {}
|
||||
for (const a of anomalyList) {
|
||||
@@ -910,6 +1010,54 @@ export default {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
/* ── 分段 Tab ── */
|
||||
.seg-tabs { margin-top: 0; }
|
||||
.seg-tab-bar {
|
||||
display: inline-flex;
|
||||
background: #f0f2f5;
|
||||
border-radius: 8px;
|
||||
padding: 3px;
|
||||
margin-bottom: 14px;
|
||||
gap: 2px;
|
||||
}
|
||||
.seg-tab-btn {
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: 6px;
|
||||
padding: 6px 20px;
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
transition: background .15s, color .15s, box-shadow .15s;
|
||||
white-space: nowrap;
|
||||
outline: none;
|
||||
}
|
||||
.seg-tab-btn:hover:not(.is-active) { background: #e4e7ed; color: #303133; }
|
||||
.seg-tab-btn.is-active {
|
||||
background: #fff;
|
||||
color: #303133;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 1px 5px rgba(0,0,0,.1);
|
||||
}
|
||||
.seg-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 16px;
|
||||
height: 16px;
|
||||
padding: 0 4px;
|
||||
background: #f56c6c;
|
||||
color: #fff;
|
||||
border-radius: 8px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
.seg-tab-pane {}
|
||||
|
||||
.param-group { margin-bottom: 20px; }
|
||||
.param-group-hd {
|
||||
display: flex;
|
||||
@@ -918,15 +1066,8 @@ export default {
|
||||
margin-bottom: 6px;
|
||||
padding: 0 2px;
|
||||
}
|
||||
.pg-segment {
|
||||
font-size: 11px;
|
||||
color: #fff;
|
||||
background: #5F7BA0;
|
||||
padding: 1px 9px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.pg-point { font-size: 13px; font-weight: 600; color: #303133; }
|
||||
.pg-code { font-size: 11px; color: #909399; margin-left: 5px; }
|
||||
.pg-code { font-size: 11px; color: #909399; }
|
||||
.val-danger { color: #f56c6c; font-weight: 600; }
|
||||
.val-ok { color: #67c23a; }
|
||||
.val-anomaly { color: #e6a23c; font-weight: 600; }
|
||||
@@ -936,6 +1077,7 @@ export default {
|
||||
font-size: 13px;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
.param-group :deep(.row-anomaly td) { background: #fff8f0 !important; }
|
||||
::v-deep .row-anomaly td { background: #fff8f8 !important; }
|
||||
::v-deep .el-drawer__header { padding: 16px 20px 12px; font-size: 15px; font-weight: 600; color: #303133; margin-bottom: 0; border-bottom: 1px solid #f0f2f5; }
|
||||
::v-deep .el-drawer__body { overflow: hidden; }
|
||||
|
||||
@@ -318,7 +318,9 @@ public class WmsMaterialCoilController extends BaseController {
|
||||
|
||||
/**
|
||||
* 钢卷溯源查询
|
||||
* 根据钢卷ID查询二维码,解析content中的steps,然后根据steps中的钢卷号反向查询数据库
|
||||
* 根据钢卷ID查询二维码,解析content中的steps,然后根据steps中的钢卷号反向查询数据库。
|
||||
* 返回 data.steps 为按二维码存储顺序排列的全量步骤(合卷场景下勿依赖原 step 序号排序,已带 display_step);
|
||||
* data.traceLayout 为前端展示结构:linear(单时间线)与 merge_join(合卷前多列并排 + 合卷汇聚)交替。
|
||||
*
|
||||
* @param coilId 钢卷ID
|
||||
* @param currentCoilNo 当前钢卷号(可选参数,用于查询特定子钢卷)
|
||||
|
||||
@@ -99,7 +99,7 @@ public interface IWmsMaterialCoilService {
|
||||
*
|
||||
* @param coilId 钢卷ID
|
||||
* @param currentCoilNo 当前钢卷号(可选,用于查询特定子钢卷)
|
||||
* @return 溯源结果(包含二维码信息和数据库记录)
|
||||
* @return 溯源结果(包含二维码信息、按存储顺序排列的 steps、traceLayout 并排展示结构、数据库记录)
|
||||
*/
|
||||
Map<String, Object> queryTrace(Long coilId, String currentCoilNo);
|
||||
|
||||
|
||||
@@ -2095,10 +2095,12 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
|
||||
// 合并所有参与合卷的原始钢卷的历史steps
|
||||
List<Map<String, Object>> steps = new ArrayList<>();
|
||||
|
||||
// 从参与合卷的原始钢卷中获取二维码信息并合并
|
||||
// 从参与合卷的原始钢卷中获取二维码信息并合并,并记录每个父卷携带的历史步数(用于溯源并排展示)
|
||||
List<Integer> parentHistStepCounts = new ArrayList<>();
|
||||
if (originalCoils != null && !originalCoils.isEmpty()) {
|
||||
for (WmsMaterialCoilBo originalCoilBo : originalCoils) {
|
||||
if (originalCoilBo.getCoilId() != null) {
|
||||
int sizeBefore = steps.size();
|
||||
// 查询原始钢卷的二维码信息
|
||||
WmsMaterialCoil originalCoil = baseMapper.selectById(originalCoilBo.getCoilId());
|
||||
if (originalCoil != null && originalCoil.getQrcodeRecordId() != null) {
|
||||
@@ -2114,6 +2116,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
|
||||
}
|
||||
}
|
||||
}
|
||||
parentHistStepCounts.add(steps.size() - sizeBefore);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2139,6 +2142,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
|
||||
}
|
||||
mergeStep.put("parent_coil_nos", String.join(",", originalCoilNos));
|
||||
mergeStep.put("parent_coil_ids", String.join(",", originalCoilIds));
|
||||
mergeStep.put("parent_hist_step_counts", parentHistStepCounts);
|
||||
mergeStep.put("new_current_coil_no", mergedCoilBo.getCurrentCoilNo());
|
||||
mergeStep.put("operator", LoginHelper.getUsername()); // 操作者
|
||||
steps.add(mergeStep);
|
||||
@@ -2468,11 +2472,14 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Map<String, Object>> steps = (List<Map<String, Object>>) contentMap.get("steps");
|
||||
|
||||
// 合卷会把多条产线的 steps 顺序拼接,各线局部 step 均为 1..n,存在重复序号;必须保留 JSON 数组顺序,不能按 step 字段排序
|
||||
List<Map<String, Object>> orderedSteps = steps != null ? new ArrayList<>(steps) : new ArrayList<>();
|
||||
|
||||
Set<String> allCoilNos = new HashSet<>();
|
||||
Set<String> operatorUsernames = new HashSet<>();
|
||||
|
||||
if (steps != null) {
|
||||
for (Map<String, Object> step : steps) {
|
||||
if (!orderedSteps.isEmpty()) {
|
||||
for (Map<String, Object> step : orderedSteps) {
|
||||
extractCoilNo(step, "current_coil_no", allCoilNos);
|
||||
extractCoilNo(step, "new_current_coil_no", allCoilNos);
|
||||
extractCoilNo(step, "old_current_coil_no", allCoilNos);
|
||||
@@ -2493,19 +2500,12 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
|
||||
|
||||
Map<String, String> operatorNicknameMap = getOperatorNicknames(operatorUsernames);
|
||||
|
||||
List<Map<String, Object>> allSteps = new ArrayList<>(steps != null ? steps : new ArrayList<>());
|
||||
|
||||
allSteps.sort((a, b) -> {
|
||||
Integer stepA = (Integer) a.get("step");
|
||||
Integer stepB = (Integer) b.get("step");
|
||||
if (stepA == null) stepA = 0;
|
||||
if (stepB == null) stepB = 0;
|
||||
return stepA.compareTo(stepB);
|
||||
});
|
||||
List<Map<String, Object>> allSteps = orderedSteps;
|
||||
|
||||
for (int i = 0; i < allSteps.size(); i++) {
|
||||
allSteps.get(i).put("display_step", i + 1);
|
||||
allSteps.get(i).put("original_step", allSteps.get(i).get("step"));
|
||||
allSteps.get(i).put("storage_index", i);
|
||||
allSteps.get(i).put("qrcode_serial", qrRecord.getSerialNumber());
|
||||
allSteps.get(i).put("qrcode_id", qrRecord.getRecordId());
|
||||
|
||||
@@ -2517,6 +2517,8 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
|
||||
}
|
||||
}
|
||||
|
||||
List<Map<String, Object>> traceLayout = buildTraceLayout(allSteps);
|
||||
|
||||
Set<String> filteredCoilNos = allCoilNos;
|
||||
if (currentCoilNo != null && !currentCoilNo.trim().isEmpty()) {
|
||||
final String filterValue = currentCoilNo;
|
||||
@@ -2567,6 +2569,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
resultMap.put("qrcode", qrRecord);
|
||||
resultMap.put("steps", allSteps);
|
||||
resultMap.put("traceLayout", traceLayout);
|
||||
resultMap.put("records", result);
|
||||
|
||||
return resultMap;
|
||||
@@ -2643,6 +2646,181 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
|
||||
}
|
||||
}
|
||||
|
||||
private static int stepNumberValue(Map<String, Object> step) {
|
||||
Object s = step.get("step");
|
||||
if (s == null) {
|
||||
return 0;
|
||||
}
|
||||
if (s instanceof Number) {
|
||||
return ((Number) s).intValue();
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(s.toString().trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isMergeTraceStep(Map<String, Object> step) {
|
||||
Object op = step.get("operation");
|
||||
return op != null && "合卷".equals(op.toString());
|
||||
}
|
||||
|
||||
private static List<String> splitCommaTokens(Object value) {
|
||||
if (value == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Arrays.stream(value.toString().split(","))
|
||||
.map(String::trim)
|
||||
.filter(s -> !s.isEmpty())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<Integer> readParentHistStepCounts(Map<String, Object> mergeStep) {
|
||||
Object raw = mergeStep.get("parent_hist_step_counts");
|
||||
if (!(raw instanceof List)) {
|
||||
return null;
|
||||
}
|
||||
List<?> list = (List<?>) raw;
|
||||
List<Integer> out = new ArrayList<>();
|
||||
for (Object o : list) {
|
||||
if (o instanceof Number) {
|
||||
out.add(((Number) o).intValue());
|
||||
} else if (o != null) {
|
||||
try {
|
||||
out.add(Integer.parseInt(o.toString().trim()));
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
out.add(0);
|
||||
}
|
||||
}
|
||||
return out.isEmpty() ? null : out;
|
||||
}
|
||||
|
||||
private static List<Integer> inferLaneSizesByStepReset(List<Map<String, Object>> buffer) {
|
||||
if (buffer == null || buffer.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Integer> lengths = new ArrayList<>();
|
||||
int runStart = 0;
|
||||
Integer prev = null;
|
||||
for (int i = 0; i < buffer.size(); i++) {
|
||||
int cur = stepNumberValue(buffer.get(i));
|
||||
if (prev != null && (cur < prev || (cur == 1 && prev > 1))) {
|
||||
lengths.add(i - runStart);
|
||||
runStart = i;
|
||||
}
|
||||
prev = cur;
|
||||
}
|
||||
lengths.add(buffer.size() - runStart);
|
||||
return lengths;
|
||||
}
|
||||
|
||||
private static List<Integer> distributeEvenly(int total, int parts) {
|
||||
if (parts <= 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Integer> r = new ArrayList<>();
|
||||
int base = total / parts;
|
||||
int rem = total % parts;
|
||||
for (int i = 0; i < parts; i++) {
|
||||
r.add(base + (i < rem ? 1 : 0));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private List<List<Map<String, Object>>> sliceBufferIntoMergeLanes(List<Map<String, Object>> buffer,
|
||||
Map<String, Object> mergeStep) {
|
||||
List<String> parentIds = splitCommaTokens(mergeStep.get("parent_coil_ids"));
|
||||
List<String> parentNos = splitCommaTokens(mergeStep.get("parent_coil_nos"));
|
||||
int n = Math.max(parentIds.size(), parentNos.size());
|
||||
if (n <= 0) {
|
||||
n = 1;
|
||||
}
|
||||
if (buffer == null) {
|
||||
buffer = Collections.emptyList();
|
||||
}
|
||||
int total = buffer.size();
|
||||
List<Integer> counts = readParentHistStepCounts(mergeStep);
|
||||
if (counts == null || counts.size() != n || counts.stream().mapToInt(Integer::intValue).sum() != total) {
|
||||
List<Integer> inferred = inferLaneSizesByStepReset(buffer);
|
||||
if (inferred.size() == n) {
|
||||
counts = inferred;
|
||||
} else {
|
||||
counts = distributeEvenly(total, n);
|
||||
}
|
||||
}
|
||||
List<List<Map<String, Object>>> lanes = new ArrayList<>();
|
||||
int offset = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
int len = i < counts.size() ? Math.max(0, counts.get(i)) : 0;
|
||||
int end = Math.min(offset + len, total);
|
||||
lanes.add(new ArrayList<>(buffer.subList(offset, end)));
|
||||
offset = end;
|
||||
}
|
||||
if (offset < total) {
|
||||
if (!lanes.isEmpty()) {
|
||||
lanes.get(lanes.size() - 1).addAll(buffer.subList(offset, total));
|
||||
} else {
|
||||
lanes.add(new ArrayList<>(buffer.subList(0, total)));
|
||||
}
|
||||
}
|
||||
return lanes;
|
||||
}
|
||||
|
||||
private static List<String> buildMergeLaneLabels(Map<String, Object> mergeStep, int laneCount) {
|
||||
List<String> nos = splitCommaTokens(mergeStep.get("parent_coil_nos"));
|
||||
List<String> ids = splitCommaTokens(mergeStep.get("parent_coil_ids"));
|
||||
List<String> labels = new ArrayList<>();
|
||||
for (int i = 0; i < laneCount; i++) {
|
||||
String no = i < nos.size() ? nos.get(i) : null;
|
||||
String id = i < ids.size() ? ids.get(i) : null;
|
||||
if (StringUtils.isNotBlank(no)) {
|
||||
labels.add(no);
|
||||
} else if (StringUtils.isNotBlank(id)) {
|
||||
labels.add("ID:" + id);
|
||||
} else {
|
||||
labels.add("来源 " + (i + 1));
|
||||
}
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将按存储顺序排列的 steps 拆成「线性段 + 合卷并排段」,供前端展示。
|
||||
*/
|
||||
private List<Map<String, Object>> buildTraceLayout(List<Map<String, Object>> orderedSteps) {
|
||||
List<Map<String, Object>> layout = new ArrayList<>();
|
||||
if (orderedSteps == null || orderedSteps.isEmpty()) {
|
||||
return layout;
|
||||
}
|
||||
List<Map<String, Object>> buffer = new ArrayList<>();
|
||||
for (Map<String, Object> s : orderedSteps) {
|
||||
if (isMergeTraceStep(s)) {
|
||||
Map<String, Object> panel = new HashMap<>();
|
||||
panel.put("type", "merge_join");
|
||||
List<List<Map<String, Object>>> lanes = sliceBufferIntoMergeLanes(new ArrayList<>(buffer), s);
|
||||
panel.put("lanes", lanes);
|
||||
panel.put("laneLabels", buildMergeLaneLabels(s, lanes.size()));
|
||||
panel.put("mergeStep", s);
|
||||
layout.add(panel);
|
||||
buffer.clear();
|
||||
} else {
|
||||
buffer.add(s);
|
||||
}
|
||||
}
|
||||
if (!buffer.isEmpty()) {
|
||||
Map<String, Object> linear = new HashMap<>();
|
||||
linear.put("type", "linear");
|
||||
linear.put("steps", new ArrayList<>(buffer));
|
||||
layout.add(linear);
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询各个库区中不同类型的钢卷分布情况
|
||||
* 按库区分组,统计每种物品类型和物品ID的钢卷数量和重量
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
-- 轧辊库
|
||||
CREATE TABLE mes_roll_info (
|
||||
roll_id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
line_id BIGINT NULL COMMENT '产线ID(关联 wms_production_line.line_id)',
|
||||
roll_no VARCHAR(50) NOT NULL COMMENT '轧辊编号',
|
||||
roll_type VARCHAR(10) NOT NULL COMMENT '辊型: WR=工作辊 / BR=支撑辊',
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'Offline' COMMENT 'Online/Standby/Offline/Scrapped',
|
||||
@@ -20,12 +21,14 @@ CREATE TABLE mes_roll_info (
|
||||
update_by VARCHAR(64), update_time DATETIME,
|
||||
remark VARCHAR(500),
|
||||
PRIMARY KEY (roll_id),
|
||||
UNIQUE KEY uk_roll_no (roll_no)
|
||||
UNIQUE KEY uk_roll_no (roll_no),
|
||||
KEY idx_line_id (line_id)
|
||||
) ENGINE=InnoDB COMMENT='轧辊库';
|
||||
|
||||
-- 换辊记录(四辊轧机,双机架)
|
||||
-- 换辊记录(四辊轧机,支持多产线)
|
||||
CREATE TABLE mes_roll_change (
|
||||
change_id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
line_id BIGINT NULL COMMENT '产线ID(关联 wms_production_line.line_id)',
|
||||
change_no VARCHAR(50) COMMENT '换辊编号',
|
||||
change_time DATETIME COMMENT '换辊时间',
|
||||
stand_no VARCHAR(10) COMMENT '机架号: 1# / 2#',
|
||||
@@ -46,12 +49,14 @@ CREATE TABLE mes_roll_change (
|
||||
update_by VARCHAR(64), update_time DATETIME,
|
||||
remark VARCHAR(500),
|
||||
PRIMARY KEY (change_id),
|
||||
KEY idx_line_stand (line_id, stand_no),
|
||||
KEY idx_stand_time (stand_no, change_time)
|
||||
) ENGINE=InnoDB COMMENT='换辊记录';
|
||||
|
||||
-- 下批轧辊(每条=一个辊位的备用辊)
|
||||
-- 下批轧辊(每条=一个辊位的备用辊,支持多产线)
|
||||
CREATE TABLE mes_roll_standby (
|
||||
standby_id BIGINT NOT NULL AUTO_INCREMENT,
|
||||
line_id BIGINT NULL COMMENT '产线ID(关联 wms_production_line.line_id)',
|
||||
stand_no VARCHAR(10) COMMENT '机架号: 1# / 2#',
|
||||
roll_no VARCHAR(50) COMMENT '轧辊编号',
|
||||
roll_type VARCHAR(10) COMMENT 'WR=工作辊 / BR=支撑辊',
|
||||
@@ -64,5 +69,6 @@ CREATE TABLE mes_roll_standby (
|
||||
create_by VARCHAR(64), create_time DATETIME,
|
||||
update_by VARCHAR(64), update_time DATETIME,
|
||||
remark VARCHAR(500),
|
||||
PRIMARY KEY (standby_id)
|
||||
PRIMARY KEY (standby_id),
|
||||
KEY idx_line_stand (line_id, stand_no)
|
||||
) ENGINE=InnoDB COMMENT='下批轧辊(待换上)';
|
||||
|
||||
Reference in New Issue
Block a user