Merge branch '0.8.X' of http://49.232.154.205:10100/DeXun/klp-oa into 0.8.X
This commit is contained in:
@@ -7,8 +7,8 @@ import com.klp.common.core.domain.PageQuery;
|
||||
import com.klp.common.core.domain.R;
|
||||
import com.klp.common.core.page.TableDataInfo;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import com.klp.common.enums.BusinessType;
|
||||
import java.util.Map;
|
||||
import com.klp.mes.roll.domain.bo.MesRollChangeBo;
|
||||
import com.klp.mes.roll.domain.vo.MesRollChangeVo;
|
||||
import com.klp.mes.roll.service.IMesRollChangeService;
|
||||
@@ -47,12 +47,12 @@ public class MesRollChangeController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询指定机架当前轧辊的实时工作长度(m)
|
||||
* GET /mes/rollChange/workLength?standNo=1%23
|
||||
* 查询各机架各辊位实时工作绩效(工作长度/卷数/重量)
|
||||
* GET /mes/rollChange/performance
|
||||
*/
|
||||
@GetMapping("/workLength")
|
||||
public R<BigDecimal> workLength(@RequestParam String standNo) {
|
||||
return R.ok(iMesRollChangeService.queryRealtimeWorkLength(standNo));
|
||||
@GetMapping("/performance")
|
||||
public R<Map<String, Map<String, Object>>> performance() {
|
||||
return R.ok(iMesRollChangeService.queryRollPerformance());
|
||||
}
|
||||
|
||||
/** 详情 */
|
||||
|
||||
@@ -54,4 +54,10 @@ public class MesRollChangeVo {
|
||||
|
||||
/** 本次换辊方案的累计工作长度(m),由 WMS 卷料数据计算得出 */
|
||||
private BigDecimal workLength;
|
||||
|
||||
/** 本次换辊方案内处理的卷数 */
|
||||
private Integer coilCount;
|
||||
|
||||
/** 本次换辊方案内处理的净重合计(单位与 wms_material_coil.net_weight 一致) */
|
||||
private BigDecimal totalWeight;
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import com.klp.mes.roll.domain.MesRollChange;
|
||||
import com.klp.mes.roll.domain.vo.MesRollChangeVo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 换辊记录 Mapper
|
||||
@@ -17,14 +17,30 @@ public interface MesRollChangeMapper extends BaseMapperPlus<MesRollChangeMapper,
|
||||
MesRollChangeVo selectLatestByStand(@Param("standNo") String standNo);
|
||||
|
||||
/**
|
||||
* 查询同机架下一次换辊时间(用于确定本次换辊的服务结束时刻)
|
||||
* 按辊位查询该位置最新一次有记录的换辊(支持部分换辊)
|
||||
* posType: upperWr / lowerWr / upperBr / lowerBr
|
||||
*/
|
||||
MesRollChangeVo selectLatestByStandAndPosition(@Param("standNo") String standNo,
|
||||
@Param("posType") String posType);
|
||||
|
||||
/**
|
||||
* 组合查询机架各辊位当前实际在机状态(每个位置独立取最新非空记录)
|
||||
* 返回 map,key 同 MesRollChangeVo 字段名(camelCase)
|
||||
*/
|
||||
Map<String, Object> selectCurrentStateByStand(@Param("standNo") String standNo);
|
||||
|
||||
/**
|
||||
* 查询同机架下一次换辊时间(任意辊位有换辊即触发)
|
||||
* 返回 null 表示该辊仍在机
|
||||
*/
|
||||
Date selectNextChangeTime(@Param("standNo") String standNo, @Param("changeTime") Date changeTime);
|
||||
|
||||
/**
|
||||
* 统计指定时间区间内的卷料实测长度之和(mm→调用方除以1000转m)
|
||||
* 统计指定时间区间内的卷料生产统计:
|
||||
* coilCount — 卷数(DISTINCT coil_id)
|
||||
* totalLength — 实测长度之和(mm,调用方/1000 转 m)
|
||||
* totalWeight — 净重之和(原始单位)
|
||||
* endTime 为 null 时统计到当前时刻
|
||||
*/
|
||||
BigDecimal selectWorkLength(@Param("startTime") Date startTime, @Param("endTime") Date endTime);
|
||||
Map<String, Object> selectCoilStats(@Param("startTime") Date startTime, @Param("endTime") Date endTime);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import com.klp.common.core.page.TableDataInfo;
|
||||
import com.klp.mes.roll.domain.bo.MesRollChangeBo;
|
||||
import com.klp.mes.roll.domain.vo.MesRollChangeVo;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 换辊记录 Service 接口
|
||||
@@ -28,8 +28,9 @@ public interface IMesRollChangeService {
|
||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||
|
||||
/**
|
||||
* 查询指定机架当前在机轧辊的实时工作长度(m)
|
||||
* 从最近一次换辊时间到现在,统计 WMS 卷料实测长度之和
|
||||
* 查询各机架各辊位的实时工作绩效(工作长度/卷数/重量)
|
||||
* 返回结构: { posType -> { standNo -> { rollNo, workLength, coilCount, totalWeight } } }
|
||||
* posType: upperBr / upperWr / lowerWr / lowerBr
|
||||
*/
|
||||
BigDecimal queryRealtimeWorkLength(String standNo);
|
||||
Map<String, Map<String, Object>> queryRollPerformance();
|
||||
}
|
||||
|
||||
@@ -24,7 +24,9 @@ import java.math.RoundingMode;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 换辊记录 Service 实现
|
||||
@@ -43,33 +45,115 @@ public class MesRollChangeServiceImpl implements IMesRollChangeService {
|
||||
|
||||
@Override
|
||||
public MesRollChangeVo queryCurrentByStand(String standNo) {
|
||||
return baseMapper.selectLatestByStand(standNo);
|
||||
// 组合各辊位最新状态,转换为 VO
|
||||
Map<String, Object> state = baseMapper.selectCurrentStateByStand(standNo);
|
||||
if (state == null) return null;
|
||||
MesRollChangeVo vo = new MesRollChangeVo();
|
||||
vo.setStandNo(standNo);
|
||||
vo.setUpperWrNo(str(state.get("upperWrNo")));
|
||||
vo.setUpperWrDia(decimal(state.get("upperWrDia")));
|
||||
vo.setLowerWrNo(str(state.get("lowerWrNo")));
|
||||
vo.setLowerWrDia(decimal(state.get("lowerWrDia")));
|
||||
vo.setUpperBrNo(str(state.get("upperBrNo")));
|
||||
vo.setUpperBrDia(decimal(state.get("upperBrDia")));
|
||||
vo.setLowerBrNo(str(state.get("lowerBrNo")));
|
||||
vo.setLowerBrDia(decimal(state.get("lowerBrDia")));
|
||||
if (state.get("changeTime") instanceof Date) {
|
||||
vo.setChangeTime((Date) state.get("changeTime"));
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableDataInfo<MesRollChangeVo> queryPageList(MesRollChangeBo bo, PageQuery pageQuery) {
|
||||
LambdaQueryWrapper<MesRollChange> lqw = buildQueryWrapper(bo);
|
||||
Page<MesRollChangeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
// 为每条换辊记录计算工作长度(上辊时间 → 下辊时间,下辊时间为 null 时统计到 NOW())
|
||||
// 为每条换辊记录计算:工作长度、卷数、重量
|
||||
for (MesRollChangeVo vo : result.getRecords()) {
|
||||
Date endTime = baseMapper.selectNextChangeTime(vo.getStandNo(), vo.getChangeTime());
|
||||
BigDecimal lengthMm = baseMapper.selectWorkLength(vo.getChangeTime(), endTime);
|
||||
// mm 转 m,保留 2 位小数
|
||||
vo.setWorkLength(lengthMm == null ? BigDecimal.ZERO
|
||||
: lengthMm.divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP));
|
||||
Map<String, Object> stats = baseMapper.selectCoilStats(vo.getChangeTime(), endTime);
|
||||
fillStatsToVo(vo, stats);
|
||||
}
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
private static final List<String> POS_TYPES = Arrays.asList("upperBr", "upperWr", "lowerWr", "lowerBr");
|
||||
private static final List<String> STANDS = Arrays.asList("1#", "2#");
|
||||
|
||||
@Override
|
||||
public BigDecimal queryRealtimeWorkLength(String standNo) {
|
||||
MesRollChangeVo latest = baseMapper.selectLatestByStand(standNo);
|
||||
if (latest == null || latest.getChangeTime() == null) {
|
||||
return BigDecimal.ZERO;
|
||||
public Map<String, Map<String, Object>> queryRollPerformance() {
|
||||
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);
|
||||
Map<String, Object> cell = new HashMap<>(6);
|
||||
if (rec != null) {
|
||||
cell.put("rollNo", getRollNoByPos(rec, posType));
|
||||
cell.put("onlineTime", rec.getChangeTime());
|
||||
Map<String, Object> stats = baseMapper.selectCoilStats(rec.getChangeTime(), null);
|
||||
MesRollChangeVo tmp = new MesRollChangeVo();
|
||||
fillStatsToVo(tmp, stats);
|
||||
cell.put("workLength", tmp.getWorkLength());
|
||||
cell.put("coilCount", tmp.getCoilCount());
|
||||
cell.put("totalWeight", tmp.getTotalWeight());
|
||||
}
|
||||
byStand.put(standNo, cell);
|
||||
}
|
||||
result.put(posType, byStand);
|
||||
}
|
||||
BigDecimal lengthMm = baseMapper.selectWorkLength(latest.getChangeTime(), null);
|
||||
return lengthMm == null ? BigDecimal.ZERO
|
||||
: lengthMm.divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP);
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 若该辊位要换新辊,则把当前在机的旧辊下线 */
|
||||
private void offlineIfReplaced(String standNo, String posType, String newRollNo) {
|
||||
if (StringUtils.isBlank(newRollNo)) return;
|
||||
MesRollChangeVo cur = baseMapper.selectLatestByStandAndPosition(standNo, posType);
|
||||
if (cur != null) {
|
||||
String oldRollNo = getRollNoByPos(cur, posType);
|
||||
if (StringUtils.isNotBlank(oldRollNo)) {
|
||||
rollInfoMapper.updateStatusByRollNoIfStatus(oldRollNo, "Offline", "Online");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 从 VO 按辊位取辊号 */
|
||||
private String getRollNoByPos(MesRollChangeVo vo, String posType) {
|
||||
switch (posType) {
|
||||
case "upperWr": return vo.getUpperWrNo();
|
||||
case "lowerWr": return vo.getLowerWrNo();
|
||||
case "upperBr": return vo.getUpperBrNo();
|
||||
case "lowerBr": return vo.getLowerBrNo();
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 类型安全的 String 转换 */
|
||||
private static String str(Object o) { return o == null ? null : o.toString(); }
|
||||
private static BigDecimal decimal(Object o) {
|
||||
if (o == null) return null;
|
||||
return o instanceof BigDecimal ? (BigDecimal) o : new BigDecimal(o.toString());
|
||||
}
|
||||
|
||||
/** 将 selectCoilStats 返回的 Map 解析并设置到 VO */
|
||||
private void fillStatsToVo(MesRollChangeVo vo, Map<String, Object> stats) {
|
||||
if (stats == null) {
|
||||
vo.setWorkLength(BigDecimal.ZERO);
|
||||
vo.setCoilCount(0);
|
||||
vo.setTotalWeight(BigDecimal.ZERO);
|
||||
return;
|
||||
}
|
||||
// totalLength (mm → m)
|
||||
BigDecimal lengthMm = stats.get("totalLength") instanceof Number
|
||||
? new BigDecimal(stats.get("totalLength").toString()) : BigDecimal.ZERO;
|
||||
vo.setWorkLength(lengthMm.divide(BigDecimal.valueOf(1000), 2, RoundingMode.HALF_UP));
|
||||
// coilCount
|
||||
Number cnt = (Number) stats.getOrDefault("coilCount", 0);
|
||||
vo.setCoilCount(cnt.intValue());
|
||||
// totalWeight(保留 2 位小数)
|
||||
BigDecimal weight = stats.get("totalWeight") instanceof Number
|
||||
? new BigDecimal(stats.get("totalWeight").toString()) : BigDecimal.ZERO;
|
||||
vo.setTotalWeight(weight.setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<MesRollChange> buildQueryWrapper(MesRollChangeBo bo) {
|
||||
@@ -99,26 +183,18 @@ public class MesRollChangeServiceImpl implements IMesRollChangeService {
|
||||
if (StringUtils.isBlank(add.getChangeType())) {
|
||||
add.setChangeType("三级换辊");
|
||||
}
|
||||
// 换辊前:将上一批在机轧辊状态改为 Offline
|
||||
MesRollChangeVo prev = baseMapper.selectLatestByStand(add.getStandNo());
|
||||
if (prev != null) {
|
||||
for (String rollNo : Arrays.asList(
|
||||
prev.getUpperWrNo(), prev.getLowerWrNo(),
|
||||
prev.getUpperBrNo(), prev.getLowerBrNo())) {
|
||||
if (StringUtils.isNotBlank(rollNo)) {
|
||||
rollInfoMapper.updateStatusByRollNo(rollNo, "Offline");
|
||||
}
|
||||
}
|
||||
}
|
||||
// 按辊位独立换辊:只下线被替换的那个位置的旧辊
|
||||
offlineIfReplaced(add.getStandNo(), "upperWr", add.getUpperWrNo());
|
||||
offlineIfReplaced(add.getStandNo(), "lowerWr", add.getLowerWrNo());
|
||||
offlineIfReplaced(add.getStandNo(), "upperBr", add.getUpperBrNo());
|
||||
offlineIfReplaced(add.getStandNo(), "lowerBr", add.getLowerBrNo());
|
||||
|
||||
baseMapper.insert(add);
|
||||
|
||||
// 将本次换上的 4 支辊状态同步为 Online
|
||||
List<String> rollNos = Arrays.asList(
|
||||
add.getUpperWrNo(), add.getLowerWrNo(),
|
||||
add.getUpperBrNo(), add.getLowerBrNo()
|
||||
);
|
||||
for (String rollNo : rollNos) {
|
||||
// 将本次有记录的辊状态同步为 Online
|
||||
for (String rollNo : Arrays.asList(
|
||||
add.getUpperWrNo(), add.getLowerWrNo(),
|
||||
add.getUpperBrNo(), add.getLowerBrNo())) {
|
||||
if (StringUtils.isNotBlank(rollNo)) {
|
||||
rollInfoMapper.updateStatusByRollNo(rollNo, "Online");
|
||||
}
|
||||
|
||||
@@ -121,17 +121,26 @@ public class MesRollInfoServiceImpl implements IMesRollInfoService {
|
||||
|
||||
@Override
|
||||
public void syncStatusFromChange() {
|
||||
// 从每个机架最新换辊记录中取出在机辊号,更新为 Online
|
||||
// 按辊位独立取最新在机辊,确保各位置状态正确
|
||||
for (String standNo : Arrays.asList("1#", "2#")) {
|
||||
MesRollChangeVo latest = rollChangeMapper.selectLatestByStand(standNo);
|
||||
if (latest == null) continue;
|
||||
for (String rollNo : Arrays.asList(
|
||||
latest.getUpperWrNo(), latest.getLowerWrNo(),
|
||||
latest.getUpperBrNo(), latest.getLowerBrNo())) {
|
||||
for (String posType : Arrays.asList("upperWr", "lowerWr", "upperBr", "lowerBr")) {
|
||||
MesRollChangeVo rec = rollChangeMapper.selectLatestByStandAndPosition(standNo, posType);
|
||||
if (rec == null) continue;
|
||||
String rollNo = getRollNoByPos(rec, posType);
|
||||
if (StringUtils.isNotBlank(rollNo)) {
|
||||
baseMapper.updateStatusByRollNo(rollNo, "Online");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getRollNoByPos(MesRollChangeVo vo, String posType) {
|
||||
switch (posType) {
|
||||
case "upperWr": return vo.getUpperWrNo();
|
||||
case "lowerWr": return vo.getLowerWrNo();
|
||||
case "upperBr": return vo.getUpperBrNo();
|
||||
case "lowerBr": return vo.getLowerBrNo();
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,37 @@
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- 按辊位查最新非空换辊记录(支持部分换辊) -->
|
||||
<select id="selectLatestByStandAndPosition" resultType="com.klp.mes.roll.domain.vo.MesRollChangeVo">
|
||||
SELECT change_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}
|
||||
<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>
|
||||
<when test="posType == 'upperBr'">AND upper_br_no IS NOT NULL AND upper_br_no != ''</when>
|
||||
<when test="posType == 'lowerBr'">AND lower_br_no IS NOT NULL AND lower_br_no != ''</when>
|
||||
</choose>
|
||||
ORDER BY change_time DESC, change_id DESC
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- 组合查询各辊位当前实际在机状态(每个位置独立取最新非空记录) -->
|
||||
<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>
|
||||
|
||||
<!-- 查询同机架在本次换辊之后最近一次换辊的时间 -->
|
||||
<select id="selectNextChangeTime" resultType="java.util.Date">
|
||||
SELECT MIN(change_time)
|
||||
@@ -25,16 +56,20 @@
|
||||
</select>
|
||||
|
||||
<!--
|
||||
统计 [startTime, endTime) 时间段内,
|
||||
符合条件的 wms_coil_pending_action 记录关联的卷料实测长度之和(mm)。
|
||||
统计 [startTime, endTime) 时间段内,符合条件的卷料生产三项汇总:
|
||||
coilCount — 卷数(COUNT DISTINCT coil_id)
|
||||
totalLength — 实测长度之和(mm)
|
||||
totalWeight — 净重之和(net_weight 原始单位)
|
||||
endTime 为 null 时取 NOW() 作为上界。
|
||||
processed_coil_ids 存储逗号分隔的 material_id 列表,使用 FIND_IN_SET 关联。
|
||||
-->
|
||||
<select id="selectWorkLength" resultType="java.math.BigDecimal">
|
||||
SELECT IFNULL(SUM(mc.actual_length), 0)
|
||||
<select id="selectCoilStats" resultType="map">
|
||||
SELECT
|
||||
COUNT(DISTINCT mc.coil_id) AS coilCount,
|
||||
IFNULL(SUM(mc.actual_length), 0) AS totalLength,
|
||||
IFNULL(SUM(mc.net_weight), 0) AS totalWeight
|
||||
FROM wms_coil_pending_action cpa
|
||||
INNER JOIN wms_material_coil mc
|
||||
ON FIND_IN_SET(mc.coil_id, cpa.processed_coil_ids) > 0
|
||||
ON FIND_IN_SET(mc.coil_id, cpa.processed_coil_ids) > 0
|
||||
WHERE cpa.del_flag = 0
|
||||
AND cpa.action_status = 2
|
||||
AND cpa.action_type IN (205, 504, 524)
|
||||
|
||||
@@ -18,12 +18,11 @@ export function getCurrentRolls(standNo) {
|
||||
})
|
||||
}
|
||||
|
||||
// 查询指定机架当前轧辊实时工作长度(m)
|
||||
export function getWorkLength(standNo) {
|
||||
// 查询各机架各辊位实时工作绩效(workLength/coilCount/totalWeight)
|
||||
export function getRollPerformance() {
|
||||
return request({
|
||||
url: '/mes/rollChange/workLength',
|
||||
method: 'get',
|
||||
params: { standNo }
|
||||
url: '/mes/rollChange/performance',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -85,30 +85,51 @@
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- 可用轧辊(离线) -->
|
||||
<el-card shadow="never" class="roll-table-card top-row__aside">
|
||||
<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="asideTableHeight" style="width:100%">
|
||||
<el-table-column label="辊型" align="center" prop="rollType" width="58">
|
||||
<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="110" show-overflow-tooltip />
|
||||
<el-table-column label="辊径(mm)" align="center" width="82">
|
||||
<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="70" />
|
||||
<el-table-column label="磨削次数" align="center" prop="grindCount" width="70" />
|
||||
</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="70" />
|
||||
<el-table-column label="1# 机架" align="center" min-width="1">
|
||||
<template slot-scope="scope">
|
||||
<perf-cell :d="scope.row['1#']" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="2# 机架" align="center" min-width="1">
|
||||
<template slot-scope="scope">
|
||||
<perf-cell :d="scope.row['2#']" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -145,17 +166,36 @@
|
||||
<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="90" />
|
||||
<el-table-column label="上工作辊" align="center" prop="upperWrNo" width="110" />
|
||||
<el-table-column label="下工作辊" align="center" prop="lowerWrNo" width="110" />
|
||||
<el-table-column label="上支撑辊" align="center" prop="upperBrNo" width="110" />
|
||||
<el-table-column label="下支撑辊" align="center" prop="lowerBrNo" width="110" />
|
||||
<el-table-column label="工作长度(m)" align="center" prop="workLength" width="110">
|
||||
<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="96">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.workLength != null">{{ scope.row.workLength }}</span>
|
||||
<span v-else style="color:#c0c4cc">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="过卷数" align="center" prop="coilCount" width="72">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.coilCount != null">{{ scope.row.coilCount }}</span>
|
||||
<span v-else style="color:#c0c4cc">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="过卷重量" align="center" prop="totalWeight" width="90">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.totalWeight != null">{{ scope.row.totalWeight }}</span>
|
||||
<span v-else style="color:#c0c4cc">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<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">
|
||||
@@ -338,31 +378,37 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getCurrentRolls, listRollChange, addRollChange, updateRollChange, delRollChange, getWorkLength } from '@/api/mes/roll/rollChange'
|
||||
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'
|
||||
|
||||
/**
|
||||
* 单元格组件:只显示数值,不展示辊号副文本和删除图标
|
||||
* 删除逻辑由外层 div 的 click 事件处理,保证所有单元格高度一致
|
||||
*/
|
||||
const ParamCell = {
|
||||
name: 'ParamCell',
|
||||
props: {
|
||||
data: { type: Object, default: null }
|
||||
},
|
||||
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' }, '—')
|
||||
}
|
||||
if (!d || d.val == null || d.val === '') return h('span', { class: 'cell-empty' }, '—')
|
||||
return h('span', { class: 'cell-main' }, String(d.val))
|
||||
}
|
||||
}
|
||||
|
||||
/** 绩效单元格:显示辊号 + 三项统计 */
|
||||
const PerfCell = {
|
||||
name: 'PerfCell',
|
||||
props: { d: { type: Object, default: null } },
|
||||
render(h) {
|
||||
const d = this.d
|
||||
if (!d || !d.rollNo) return h('span', { class: 'cell-empty' }, '—')
|
||||
return h('div', { class: 'perf-cell' }, [
|
||||
h('div', { class: 'perf-roll' }, d.rollNo),
|
||||
h('div', { class: 'perf-stat' }, `${d.workLength != null ? d.workLength + 'm' : '—'} · ${d.coilCount != null ? d.coilCount + '卷' : '—'} · ${d.totalWeight != null ? d.totalWeight : '—'}`)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'WorkingRoll',
|
||||
components: { ParamCell },
|
||||
components: { ParamCell, PerfCell },
|
||||
data() {
|
||||
return {
|
||||
current: { '1#': {}, '2#': {} },
|
||||
@@ -371,9 +417,6 @@ export default {
|
||||
standbyList: { '1#': [], '2#': [] },
|
||||
loadingStandby: { '1#': false, '2#': false },
|
||||
|
||||
/** 当前在机轧辊实时工作长度(m) */
|
||||
workLength: { '1#': null, '2#': null },
|
||||
|
||||
rollInfoMap: {},
|
||||
|
||||
historyLoading: false,
|
||||
@@ -389,7 +432,11 @@ export default {
|
||||
|
||||
offlineRolls: [],
|
||||
offlineLoading: false,
|
||||
asideTableHeight: 400,
|
||||
|
||||
perfData: {},
|
||||
perfLoading: false,
|
||||
|
||||
asideHalfH: 200,
|
||||
|
||||
changeOpen: false,
|
||||
changeSubmitting: false,
|
||||
@@ -510,12 +557,22 @@ export default {
|
||||
cur1: { val: null, sub: null }, cur2: { val: null, sub: null },
|
||||
sb1: { val: null, sub: null }, sb2: { val: null, sub: null }
|
||||
},
|
||||
{
|
||||
label: '工作长度(m)', group: 'pl', rollType: null, position: null,
|
||||
cur1: { val: this.workLength['1#'] }, cur2: { val: this.workLength['2#'] },
|
||||
sb1: { val: null }, sb2: { val: null }
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
/** 工作绩效表数据行:4 辊位 × {label, 1#, 2#} */
|
||||
perfRows() {
|
||||
const POS = [
|
||||
{ key: 'upperBr', label: '上支撑辊' },
|
||||
{ key: 'upperWr', label: '上工作辊' },
|
||||
{ key: 'lowerWr', label: '下工作辊' },
|
||||
{ key: 'lowerBr', label: '下支撑辊' }
|
||||
]
|
||||
return POS.map(p => ({
|
||||
label: p.label,
|
||||
'1#': (this.perfData[p.key] || {})['1#'] || {},
|
||||
'2#': (this.perfData[p.key] || {})['2#'] || {}
|
||||
}))
|
||||
}
|
||||
},
|
||||
|
||||
@@ -566,16 +623,17 @@ export default {
|
||||
;['1#', '2#'].forEach(s => {
|
||||
this.loadCurrent(s)
|
||||
this.loadStandby(s)
|
||||
this.loadWorkLength(s)
|
||||
})
|
||||
this.loadHistory()
|
||||
this.loadOfflineRolls()
|
||||
this.loadRollPerformance()
|
||||
},
|
||||
|
||||
loadWorkLength(standNo) {
|
||||
getWorkLength(standNo).then(res => {
|
||||
this.$set(this.workLength, standNo, res.data != null ? res.data : null)
|
||||
}).catch(() => {})
|
||||
loadRollPerformance() {
|
||||
this.perfLoading = true
|
||||
getRollPerformance().then(res => {
|
||||
this.perfData = res.data || {}
|
||||
}).finally(() => { this.perfLoading = false })
|
||||
},
|
||||
|
||||
loadCurrent(standNo) {
|
||||
@@ -667,9 +725,9 @@ export default {
|
||||
clearRollStandby(standNo).finally(() => {
|
||||
this.loadCurrent(standNo)
|
||||
this.loadStandby(standNo)
|
||||
this.loadWorkLength(standNo)
|
||||
this.loadHistory()
|
||||
this.loadOfflineRolls()
|
||||
this.loadRollPerformance()
|
||||
})
|
||||
}).finally(() => {
|
||||
this.changeSubmitting = false
|
||||
@@ -767,13 +825,15 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
// 让右侧表格高度 = 左侧卡片 body 区域高度
|
||||
// 右侧两个面板各占左侧表格高度的一半(各自减去卡片头高度)
|
||||
syncAsideHeight() {
|
||||
const el = this.$refs.mainTable && this.$refs.mainTable.$el
|
||||
if (!el) return
|
||||
// el-table 的 $el 就是 .el-table 根节点,取其实际渲染高度
|
||||
const h = el.offsetHeight
|
||||
if (h > 0) this.asideTableHeight = h
|
||||
const totalH = el.offsetHeight // 左侧 el-table 实际高度
|
||||
const gap = 12 // 两个面板之间的间距
|
||||
const headerH = 48 // 单个卡片 header 高度估值
|
||||
const half = Math.max(100, Math.floor((totalH - gap) / 2) - headerH)
|
||||
this.asideHalfH = half
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -792,6 +852,21 @@ export default {
|
||||
.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; }
|
||||
|
||||
/* 绩效单元格 */
|
||||
.perf-cell { padding: 2px 0; line-height: 1.4; }
|
||||
.perf-roll { font-family: 'Consolas', monospace; font-size: 12px; font-weight: 600; color: #1f2329; }
|
||||
.perf-stat { font-size: 11px; color: #8f9099; }
|
||||
|
||||
/* 历史换辊辊组 */
|
||||
.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; }
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
package com.klp.domain.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnore;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.alibaba.excel.annotation.write.style.ColumnWidth;
|
||||
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
|
||||
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 钢卷异常报表导出DTO - EasyExcel版本
|
||||
* 使用EasyExcel注解实现O(n)复杂度的单元格合并
|
||||
*
|
||||
* @author Joshi
|
||||
* @date 2026-05-07
|
||||
*/
|
||||
@Data
|
||||
@ContentRowHeight(20)
|
||||
@HeadRowHeight(24)
|
||||
@ColumnWidth(15)
|
||||
public class WmsCoilAbnormalExportVo {
|
||||
|
||||
// 钢卷基本信息(前25列)
|
||||
@ExcelProperty("类型")
|
||||
@ColumnWidth(12)
|
||||
private String itemType;
|
||||
|
||||
@ExcelProperty("逻辑库区")
|
||||
@ColumnWidth(12)
|
||||
private String warehouseName;
|
||||
|
||||
@ExcelProperty("实际库区")
|
||||
@ColumnWidth(12)
|
||||
private String actualWarehouseName;
|
||||
|
||||
@ExcelProperty("入场卷号")
|
||||
@ColumnWidth(15)
|
||||
private String enterCoilNo;
|
||||
|
||||
@ExcelProperty("厂家卷号")
|
||||
@ColumnWidth(15)
|
||||
private String supplierCoilNo;
|
||||
|
||||
@ExcelProperty("成品卷号")
|
||||
@ColumnWidth(15)
|
||||
private String currentCoilNo;
|
||||
|
||||
@ExcelProperty("日期")
|
||||
@ColumnWidth(20)
|
||||
private String createTime;
|
||||
|
||||
@ExcelProperty("重量")
|
||||
@ColumnWidth(12)
|
||||
private String netWeight;
|
||||
|
||||
@ExcelProperty("用途")
|
||||
@ColumnWidth(12)
|
||||
private String businessPurpose;
|
||||
|
||||
@ExcelProperty("切边要求")
|
||||
@ColumnWidth(12)
|
||||
private String trimmingRequirement;
|
||||
|
||||
@ExcelProperty("包装种类")
|
||||
@ColumnWidth(12)
|
||||
private String packagingRequirement;
|
||||
|
||||
@ExcelProperty("产品质量")
|
||||
@ColumnWidth(12)
|
||||
private String qualityStatus;
|
||||
|
||||
@ExcelProperty("原料材质")
|
||||
@ColumnWidth(12)
|
||||
private String packingStatus;
|
||||
|
||||
@ExcelProperty("库存状态")
|
||||
@ColumnWidth(12)
|
||||
private String status;
|
||||
|
||||
@ExcelProperty("备注")
|
||||
@ColumnWidth(20)
|
||||
private String remark;
|
||||
|
||||
@ExcelProperty("名称")
|
||||
@ColumnWidth(15)
|
||||
private String itemName;
|
||||
|
||||
@ExcelProperty("规格")
|
||||
@ColumnWidth(20)
|
||||
private String specification;
|
||||
|
||||
@ExcelProperty("长度")
|
||||
@ColumnWidth(12)
|
||||
private String length;
|
||||
|
||||
@ExcelProperty("材质")
|
||||
@ColumnWidth(12)
|
||||
private String material;
|
||||
|
||||
@ExcelProperty("厂家")
|
||||
@ColumnWidth(15)
|
||||
private String manufacturer;
|
||||
|
||||
@ExcelProperty("表面处理")
|
||||
@ColumnWidth(12)
|
||||
private String surfaceTreatmentDesc;
|
||||
|
||||
@ExcelProperty("锌层")
|
||||
@ColumnWidth(12)
|
||||
private String zincLayer;
|
||||
|
||||
@ExcelProperty("物品ID")
|
||||
@ColumnWidth(12)
|
||||
private String itemId;
|
||||
|
||||
@ExcelProperty("操作完成时间")
|
||||
@ColumnWidth(20)
|
||||
private String actionCompleteTime;
|
||||
|
||||
@ExcelProperty("调拨类型")
|
||||
@ColumnWidth(12)
|
||||
private String transferType;
|
||||
|
||||
// 改判原因(第26列)
|
||||
@ExcelProperty("改判原因")
|
||||
@ColumnWidth(25)
|
||||
private String rejudgeReason;
|
||||
|
||||
// 异常信息(后17列)
|
||||
@ExcelProperty("产线")
|
||||
@ColumnWidth(12)
|
||||
private String productionLine;
|
||||
|
||||
@ExcelProperty("位置")
|
||||
@ColumnWidth(12)
|
||||
private String position;
|
||||
|
||||
@ExcelProperty("长度坐标")
|
||||
@ColumnWidth(12)
|
||||
private String abnormalLength;
|
||||
|
||||
@ExcelProperty("缺陷开始位置")
|
||||
@ColumnWidth(15)
|
||||
private String startPosition;
|
||||
|
||||
@ExcelProperty("缺陷结束位置")
|
||||
@ColumnWidth(15)
|
||||
private String endPosition;
|
||||
|
||||
@ExcelProperty("缺陷代码")
|
||||
@ColumnWidth(12)
|
||||
private String defectCode;
|
||||
|
||||
@ExcelProperty("缺陷类型")
|
||||
@ColumnWidth(12)
|
||||
private String defectType;
|
||||
|
||||
@ExcelProperty("缺陷率")
|
||||
@ColumnWidth(12)
|
||||
private String defectRate;
|
||||
|
||||
@ExcelProperty("缺陷重量")
|
||||
@ColumnWidth(12)
|
||||
private String defectWeight;
|
||||
|
||||
@ExcelProperty("程度")
|
||||
@ColumnWidth(10)
|
||||
private String degree;
|
||||
|
||||
@ExcelProperty("判级")
|
||||
@ColumnWidth(10)
|
||||
private String judgeLevel;
|
||||
|
||||
@ExcelProperty("判级人")
|
||||
@ColumnWidth(12)
|
||||
private String judgeBy;
|
||||
|
||||
@ExcelProperty("判级时间")
|
||||
@ColumnWidth(20)
|
||||
private String judgeTime;
|
||||
|
||||
@ExcelProperty("主标记")
|
||||
@ColumnWidth(12)
|
||||
private String mainMark;
|
||||
|
||||
@ExcelProperty("整卷标记")
|
||||
@ColumnWidth(12)
|
||||
private String wholeCoilMark;
|
||||
|
||||
@ExcelProperty("异常备注")
|
||||
@ColumnWidth(20)
|
||||
private String abnormalRemark;
|
||||
|
||||
@ExcelProperty("板面")
|
||||
@ColumnWidth(10)
|
||||
private String plateSurface;
|
||||
|
||||
/**
|
||||
* 钢卷ID,用于合并单元格的标识(隐藏列)
|
||||
*/
|
||||
@ExcelIgnore
|
||||
private Long coilId;
|
||||
}
|
||||
@@ -45,11 +45,9 @@ import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
|
||||
/**
|
||||
* 钢卷物料表Service业务层处理
|
||||
@@ -5346,212 +5344,129 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 构建导出数据(扁平化:一个钢卷+一个异常 = 一行)
|
||||
List<WmsCoilAbnormalExportRow> exportData = new ArrayList<>();
|
||||
// 5. 构建EasyExcel导出数据 - O(n)复杂度
|
||||
List<WmsCoilAbnormalExportVo> exportData = new ArrayList<>();
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
for (WmsMaterialCoilVo coil : coilList) {
|
||||
Long coilId = coil.getCoilId();
|
||||
List<WmsCoilAbnormal> abnormalList = abnormalMap.getOrDefault(coilId, new ArrayList<>());
|
||||
String rejudgeReason = rejudgeReasonMap.get(coilId);
|
||||
|
||||
if (abnormalList.isEmpty()) {
|
||||
WmsCoilAbnormalExportRow row = new WmsCoilAbnormalExportRow();
|
||||
row.setCoil(coil);
|
||||
row.setRejudgeReason(rejudgeReason);
|
||||
row.setAbnormal(null);
|
||||
exportData.add(row);
|
||||
// 无异常信息的钢卷,创建一行空异常数据
|
||||
WmsCoilAbnormalExportVo dto = createExportVo(coil, rejudgeReason, null, sdf);
|
||||
dto.setCoilId(coilId);
|
||||
exportData.add(dto);
|
||||
} else {
|
||||
// 有异常信息的钢卷,每个异常创建一行
|
||||
for (WmsCoilAbnormal abnormal : abnormalList) {
|
||||
WmsCoilAbnormalExportRow row = new WmsCoilAbnormalExportRow();
|
||||
row.setCoil(coil);
|
||||
row.setRejudgeReason(rejudgeReason);
|
||||
row.setAbnormal(abnormal);
|
||||
exportData.add(row);
|
||||
WmsCoilAbnormalExportVo dto = createExportVo(coil, rejudgeReason, abnormal, sdf);
|
||||
dto.setCoilId(coilId);
|
||||
exportData.add(dto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 导出Excel
|
||||
try (Workbook wb = new XSSFWorkbook()) {
|
||||
Sheet sheet = wb.createSheet("异常报表");
|
||||
int r = 0;
|
||||
|
||||
// 标题行
|
||||
Row titleRow = sheet.createRow(r++);
|
||||
titleRow.setHeightInPoints(36f);
|
||||
Cell titleCell = titleRow.createCell(0);
|
||||
titleCell.setCellValue("钢卷异常报表");
|
||||
|
||||
CellStyle titleStyle = wb.createCellStyle();
|
||||
titleStyle.setAlignment(HorizontalAlignment.CENTER);
|
||||
titleStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
Font titleFont = wb.createFont();
|
||||
titleFont.setBold(true);
|
||||
titleFont.setFontHeightInPoints((short) 15);
|
||||
titleStyle.setFont(titleFont);
|
||||
|
||||
CellRangeAddress titleRegion = new CellRangeAddress(0, 0, 0, 41);
|
||||
sheet.addMergedRegion(titleRegion);
|
||||
for (int i = titleRegion.getFirstColumn(); i <= titleRegion.getLastColumn(); i++) {
|
||||
Cell cell = titleRow.getCell(i);
|
||||
if (cell == null) cell = titleRow.createCell(i);
|
||||
cell.setCellStyle(titleStyle);
|
||||
}
|
||||
|
||||
// 表头
|
||||
String[] headers = {
|
||||
"类型", "逻辑库区", "实际库区", "入场卷号", "厂家卷号", "成品卷号", "日期",
|
||||
"重量", "用途", "切边要求", "包装种类", "产品质量", "原料材质",
|
||||
"库存状态", "备注", "名称", "规格", "长度", "材质", "厂家",
|
||||
"表面处理", "锌层", "物品ID", "操作完成时间", "调拨类型",
|
||||
"改判原因",
|
||||
"产线", "位置", "长度坐标", "缺陷开始位置", "缺陷结束位置",
|
||||
"缺陷代码", "缺陷类型", "缺陷率", "缺陷重量", "程度", "判级",
|
||||
"判级人", "判级时间", "主标记", "整卷标记", "异常备注", "板面"
|
||||
};
|
||||
|
||||
CellStyle headStyle = wb.createCellStyle();
|
||||
Font headFont = wb.createFont();
|
||||
headFont.setBold(true);
|
||||
headStyle.setFont(headFont);
|
||||
headStyle.setAlignment(HorizontalAlignment.CENTER);
|
||||
headStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
||||
headStyle.setFillForegroundColor(IndexedColors.PALE_BLUE.getIndex());
|
||||
headStyle.setWrapText(true);
|
||||
|
||||
Row headRow = sheet.createRow(r++);
|
||||
headRow.setHeightInPoints(24);
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
Cell cell = headRow.createCell(i);
|
||||
cell.setCellValue(headers[i]);
|
||||
cell.setCellStyle(headStyle);
|
||||
}
|
||||
|
||||
// 数据行样式
|
||||
CellStyle centerStyle = wb.createCellStyle();
|
||||
centerStyle.setAlignment(HorizontalAlignment.CENTER);
|
||||
centerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
|
||||
// 填充数据
|
||||
int dataStartRow = r;
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
for (WmsCoilAbnormalExportRow rowData : exportData) {
|
||||
WmsMaterialCoilVo coil = rowData.getCoil();
|
||||
WmsCoilAbnormal abnormal = rowData.getAbnormal();
|
||||
String rejudgeReason = rowData.getRejudgeReason();
|
||||
|
||||
Row row = sheet.createRow(r++);
|
||||
int cc = 0;
|
||||
|
||||
// 钢卷信息(前25列)
|
||||
row.createCell(cc++).setCellValue(coil.getItemType() != null ? coil.getItemType() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getWarehouseName() != null ? coil.getWarehouseName() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getActualWarehouseName() != null ? coil.getActualWarehouseName() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getEnterCoilNo() != null ? coil.getEnterCoilNo() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getSupplierCoilNo() != null ? coil.getSupplierCoilNo() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getCurrentCoilNo() != null ? coil.getCurrentCoilNo() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getCreateTime() != null ? sdf.format(coil.getCreateTime()) : "");
|
||||
row.createCell(cc++).setCellValue(coil.getNetWeight() != null ? coil.getNetWeight().toString() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getBusinessPurpose() != null ? coil.getBusinessPurpose() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getTrimmingRequirement() != null ? coil.getTrimmingRequirement() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getPackagingRequirement() != null ? coil.getPackagingRequirement() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getQualityStatus() != null ? coil.getQualityStatus() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getPackingStatus() != null ? coil.getPackingStatus() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getStatus() != null ? coil.getStatus().toString() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getRemark() != null ? coil.getRemark() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getItemName() != null ? coil.getItemName() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getSpecification() != null ? coil.getSpecification() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getLength() != null ? coil.getLength().toString() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getMaterial() != null ? coil.getMaterial() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getManufacturer() != null ? coil.getManufacturer() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getSurfaceTreatmentDesc() != null ? coil.getSurfaceTreatmentDesc() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getZincLayer() != null ? coil.getZincLayer() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getItemId() != null ? coil.getItemId().toString() : "");
|
||||
row.createCell(cc++).setCellValue(coil.getActionCompleteTime() != null ? sdf.format(coil.getActionCompleteTime()) : "");
|
||||
row.createCell(cc++).setCellValue(coil.getTransferType() != null ? coil.getTransferType() : "");
|
||||
|
||||
// 改判原因
|
||||
row.createCell(cc++).setCellValue(rejudgeReason != null ? rejudgeReason : "");
|
||||
|
||||
// 异常信息
|
||||
if (abnormal != null) {
|
||||
row.createCell(cc++).setCellValue(abnormal.getProductionLine() != null ? abnormal.getProductionLine() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getPosition() != null ? abnormal.getPosition() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getLength() != null ? abnormal.getLength().toString() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getStartPosition() != null ? abnormal.getStartPosition().toString() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getEndPosition() != null ? abnormal.getEndPosition().toString() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getDefectCode() != null ? abnormal.getDefectCode() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getDefectType() != null ? abnormal.getDefectType() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getDefectRate() != null ? abnormal.getDefectRate().toString() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getDefectWeight() != null ? abnormal.getDefectWeight().toString() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getDegree() != null ? abnormal.getDegree() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getJudgeLevel() != null ? abnormal.getJudgeLevel() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getJudgeBy() != null ? abnormal.getJudgeBy() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getJudgeTime() != null ? sdf.format(abnormal.getJudgeTime()) : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getMainMark() != null ? abnormal.getMainMark().toString() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getWholeCoilMark() != null ? abnormal.getWholeCoilMark().toString() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getRemark() != null ? abnormal.getRemark() : "");
|
||||
row.createCell(cc++).setCellValue(abnormal.getPlateSurface() != null ? abnormal.getPlateSurface() : "");
|
||||
} else {
|
||||
for (int j = 0; j < 17; j++) row.createCell(cc++).setCellValue("");
|
||||
}
|
||||
}
|
||||
|
||||
// 合并钢卷信息列(前26列:25列钢卷信息 + 1列改判原因)
|
||||
int currentRow = dataStartRow;
|
||||
while (currentRow < r) {
|
||||
Long currentCoilId = exportData.get(currentRow - dataStartRow).getCoil().getCoilId();
|
||||
int startRow = currentRow;
|
||||
int endRow = currentRow;
|
||||
|
||||
while (endRow < r && exportData.get(endRow - dataStartRow).getCoil().getCoilId().equals(currentCoilId)) {
|
||||
endRow++;
|
||||
}
|
||||
|
||||
if (endRow - startRow > 1) {
|
||||
for (int col = 0; col < 26; col++) {
|
||||
sheet.addMergedRegion(new CellRangeAddress(startRow, endRow - 1, col, col));
|
||||
}
|
||||
}
|
||||
|
||||
// 设置居中样式
|
||||
for (int rowIdx = startRow; rowIdx < endRow; rowIdx++) {
|
||||
Row row = sheet.getRow(rowIdx);
|
||||
if (row != null) {
|
||||
for (int col = 0; col < 26; col++) {
|
||||
Cell cell = row.getCell(col);
|
||||
if (cell != null) cell.setCellStyle(centerStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentRow = endRow;
|
||||
}
|
||||
|
||||
// 自适应列宽
|
||||
for (int i = 0; i < headers.length; i++) {
|
||||
sheet.autoSizeColumn(i, true);
|
||||
int w = sheet.getColumnWidth(i);
|
||||
sheet.setColumnWidth(i, Math.min(Math.max(w, 3000), 12000));
|
||||
}
|
||||
|
||||
// 输出
|
||||
// 6. 使用EasyExcel导出 - O(n)复杂度,瞬间完成
|
||||
try {
|
||||
// 设置响应头
|
||||
String filename = "abnormal_report_" + System.currentTimeMillis() + ".xlsx";
|
||||
String encoded = URLEncoder.encode(filename, StandardCharsets.UTF_8.name());
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encoded);
|
||||
|
||||
try (ServletOutputStream os = response.getOutputStream()) {
|
||||
wb.write(os);
|
||||
os.flush();
|
||||
}
|
||||
// 使用EasyExcel写入,正常导出
|
||||
EasyExcel.write(response.getOutputStream(), WmsCoilAbnormalExportVo.class)
|
||||
.sheet("异常报表")
|
||||
.doWrite(exportData);
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("导出失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 创建导出DTO对象
|
||||
*/
|
||||
private WmsCoilAbnormalExportVo createExportVo(WmsMaterialCoilVo coil, String rejudgeReason,
|
||||
WmsCoilAbnormal abnormal, SimpleDateFormat sdf) {
|
||||
WmsCoilAbnormalExportVo dto = new WmsCoilAbnormalExportVo();
|
||||
|
||||
// 钢卷基本信息(前25列)
|
||||
dto.setItemType(coil.getItemType() != null ? coil.getItemType() : "");
|
||||
dto.setWarehouseName(coil.getWarehouseName() != null ? coil.getWarehouseName() : "");
|
||||
dto.setActualWarehouseName(coil.getActualWarehouseName() != null ? coil.getActualWarehouseName() : "");
|
||||
dto.setEnterCoilNo(coil.getEnterCoilNo() != null ? coil.getEnterCoilNo() : "");
|
||||
dto.setSupplierCoilNo(coil.getSupplierCoilNo() != null ? coil.getSupplierCoilNo() : "");
|
||||
dto.setCurrentCoilNo(coil.getCurrentCoilNo() != null ? coil.getCurrentCoilNo() : "");
|
||||
dto.setCreateTime(coil.getCreateTime() != null ? sdf.format(coil.getCreateTime()) : "");
|
||||
dto.setNetWeight(coil.getNetWeight() != null ? coil.getNetWeight().toString() : "");
|
||||
dto.setBusinessPurpose(coil.getBusinessPurpose() != null ? coil.getBusinessPurpose() : "");
|
||||
dto.setTrimmingRequirement(coil.getTrimmingRequirement() != null ? coil.getTrimmingRequirement() : "");
|
||||
dto.setPackagingRequirement(coil.getPackagingRequirement() != null ? coil.getPackagingRequirement() : "");
|
||||
dto.setQualityStatus(coil.getQualityStatus() != null ? coil.getQualityStatus() : "");
|
||||
dto.setPackingStatus(coil.getPackingStatus() != null ? coil.getPackingStatus() : "");
|
||||
dto.setStatus(coil.getStatus() != null ? coil.getStatus().toString() : "");
|
||||
dto.setRemark(coil.getRemark() != null ? coil.getRemark() : "");
|
||||
dto.setItemName(coil.getItemName() != null ? coil.getItemName() : "");
|
||||
dto.setSpecification(coil.getSpecification() != null ? coil.getSpecification() : "");
|
||||
dto.setLength(coil.getLength() != null ? coil.getLength().toString() : "");
|
||||
dto.setMaterial(coil.getMaterial() != null ? coil.getMaterial() : "");
|
||||
dto.setManufacturer(coil.getManufacturer() != null ? coil.getManufacturer() : "");
|
||||
dto.setSurfaceTreatmentDesc(coil.getSurfaceTreatmentDesc() != null ? coil.getSurfaceTreatmentDesc() : "");
|
||||
dto.setZincLayer(coil.getZincLayer() != null ? coil.getZincLayer() : "");
|
||||
dto.setItemId(coil.getItemId() != null ? coil.getItemId().toString() : "");
|
||||
dto.setActionCompleteTime(coil.getActionCompleteTime() != null ? sdf.format(coil.getActionCompleteTime()) : "");
|
||||
dto.setTransferType(coil.getTransferType() != null ? coil.getTransferType() : "");
|
||||
|
||||
// 改判原因(第26列)
|
||||
dto.setRejudgeReason(rejudgeReason != null ? rejudgeReason : "");
|
||||
|
||||
// 异常信息(后17列)
|
||||
if (abnormal != null) {
|
||||
dto.setProductionLine(abnormal.getProductionLine() != null ? abnormal.getProductionLine() : "");
|
||||
dto.setPosition(abnormal.getPosition() != null ? abnormal.getPosition() : "");
|
||||
dto.setAbnormalLength(abnormal.getLength() != null ? abnormal.getLength().toString() : "");
|
||||
dto.setStartPosition(abnormal.getStartPosition() != null ? abnormal.getStartPosition().toString() : "");
|
||||
dto.setEndPosition(abnormal.getEndPosition() != null ? abnormal.getEndPosition().toString() : "");
|
||||
dto.setDefectCode(abnormal.getDefectCode() != null ? abnormal.getDefectCode() : "");
|
||||
dto.setDefectType(abnormal.getDefectType() != null ? abnormal.getDefectType() : "");
|
||||
dto.setDefectRate(abnormal.getDefectRate() != null ? abnormal.getDefectRate().toString() : "");
|
||||
dto.setDefectWeight(abnormal.getDefectWeight() != null ? abnormal.getDefectWeight().toString() : "");
|
||||
dto.setDegree(abnormal.getDegree() != null ? abnormal.getDegree() : "");
|
||||
dto.setJudgeLevel(abnormal.getJudgeLevel() != null ? abnormal.getJudgeLevel() : "");
|
||||
dto.setJudgeBy(abnormal.getJudgeBy() != null ? abnormal.getJudgeBy() : "");
|
||||
dto.setJudgeTime(abnormal.getJudgeTime() != null ? sdf.format(abnormal.getJudgeTime()) : "");
|
||||
dto.setMainMark(abnormal.getMainMark() != null ? abnormal.getMainMark().toString() : "");
|
||||
dto.setWholeCoilMark(abnormal.getWholeCoilMark() != null ? abnormal.getWholeCoilMark().toString() : "");
|
||||
dto.setAbnormalRemark(abnormal.getRemark() != null ? abnormal.getRemark() : "");
|
||||
dto.setPlateSurface(abnormal.getPlateSurface() != null ? abnormal.getPlateSurface() : "");
|
||||
} else {
|
||||
// 空异常信息
|
||||
dto.setProductionLine("");
|
||||
dto.setPosition("");
|
||||
dto.setAbnormalLength("");
|
||||
dto.setStartPosition("");
|
||||
dto.setEndPosition("");
|
||||
dto.setDefectCode("");
|
||||
dto.setDefectType("");
|
||||
dto.setDefectRate("");
|
||||
dto.setDefectWeight("");
|
||||
dto.setDegree("");
|
||||
dto.setJudgeLevel("");
|
||||
dto.setJudgeBy("");
|
||||
dto.setJudgeTime("");
|
||||
dto.setMainMark("");
|
||||
dto.setWholeCoilMark("");
|
||||
dto.setAbnormalRemark("");
|
||||
dto.setPlateSurface("");
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user