diff --git a/src/main/java/com/xujie/sys/modules/sampletracking/service/impl/SampleProofTrackingServiceImpl.java b/src/main/java/com/xujie/sys/modules/sampletracking/service/impl/SampleProofTrackingServiceImpl.java index e2786370..7a3f4a4f 100644 --- a/src/main/java/com/xujie/sys/modules/sampletracking/service/impl/SampleProofTrackingServiceImpl.java +++ b/src/main/java/com/xujie/sys/modules/sampletracking/service/impl/SampleProofTrackingServiceImpl.java @@ -1,6 +1,12 @@ package com.xujie.sys.modules.sampletracking.service.impl; import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.alibaba.excel.write.builder.ExcelWriterBuilder; +import com.alibaba.excel.write.handler.CellWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.metadata.style.WriteFont; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; @@ -30,6 +36,8 @@ import com.xujie.sys.modules.sampletracking.service.SampleProofTrackingService; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanWrapperImpl; @@ -52,6 +60,8 @@ import java.util.Objects; import java.util.Set; import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Workbook; /** * 产品打样记录跟踪 Service 实现 @@ -138,6 +148,15 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic normalizedBuNo, buProcessConfigMap ); + int exportHeaderRowCount = resolveExportHeaderRowCount(headerRows); + Set completedProcessCellKeySet = collectCompletedProcessCellKeySet( + exportRows, + trackingColumns, + processColumns, + normalizedBuNo, + buProcessConfigMap, + exportHeaderRowCount + ); try { String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); String fileName = URLEncoder.encode("项目打样跟踪_" + timestamp, StandardCharsets.UTF_8.name()) @@ -170,10 +189,15 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic HorizontalCellStyleStrategy noHeaderBackgroundStyle = new HorizontalCellStyleStrategy(headCellStyle, contentCellStyle); - EasyExcel.write(response.getOutputStream()) + ExcelWriterBuilder writerBuilder = EasyExcel.write(response.getOutputStream()) .autoCloseStream(false) .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) - .registerWriteHandler(noHeaderBackgroundStyle) + .registerWriteHandler(noHeaderBackgroundStyle); + if (!completedProcessCellKeySet.isEmpty()) { + // 已完成工序单元格灰底高亮,导出视觉与前端表格“完成态”保持一致。 + writerBuilder.registerWriteHandler(new CompletedProcessCellStyleHandler(completedProcessCellKeySet)); + } + writerBuilder .head(headerRows) .sheet("项目打样跟踪") .doWrite(bodyRows); @@ -302,6 +326,22 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic return row; } + private int resolveExportHeaderRowCount(List> headerRows) { + int rowCount = 0; + if (headerRows == null || headerRows.isEmpty()) { + return rowCount; + } + for (List oneHeaderRow : headerRows) { + if (oneHeaderRow == null || oneHeaderRow.isEmpty()) { + continue; + } + if (oneHeaderRow.size() > rowCount) { + rowCount = oneHeaderRow.size(); + } + } + return rowCount; + } + private List> buildExportBodyRows(List exportRows, List trackingColumns, List processColumns, @@ -328,6 +368,40 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic return rows; } + private Set collectCompletedProcessCellKeySet(List exportRows, + List trackingColumns, + List processColumns, + String selectedBuNo, + Map buProcessConfigMap, + int headerRowCount) { + Set rows = new LinkedHashSet<>(); + if (exportRows == null || exportRows.isEmpty() || processColumns == null || processColumns.isEmpty()) { + return rows; + } + int trackingColumnSize = trackingColumns == null ? 0 : trackingColumns.size(); + for (int rowIndex = 0; rowIndex < exportRows.size(); rowIndex++) { + ProofTrackingData rowData = exportRows.get(rowIndex); + for (int processIndex = 0; processIndex < processColumns.size(); processIndex++) { + ProofTrackingProcessColumnData processColumn = processColumns.get(processIndex); + if (processColumn == null || isBlank(processColumn.getCode())) { + continue; + } + if (!isProcessColumnVisibleForRow(rowData, processColumn, processColumns.size(), selectedBuNo, buProcessConfigMap)) { + continue; + } + if (!isProcessCompleteForExport(rowData, processColumn)) { + continue; + } + rows.add(buildExportCellCoordinateKey(headerRowCount + rowIndex, trackingColumnSize + processIndex)); + } + } + return rows; + } + + private static long buildExportCellCoordinateKey(int rowIndex, int columnIndex) { + return (((long) rowIndex) << 32) | (columnIndex & 0xffffffffL); + } + private String resolveTrackingColumnExportValue(ProofTrackingData rowData, ProofTrackingExportColumnData trackingColumn, List processColumns, @@ -338,7 +412,7 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic } String columnProp = trackingColumn.getColumnProp().trim(); if (rowData == null) { - return "-"; + return ""; } switch (columnProp) { case "proofingStatus": @@ -376,9 +450,16 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic List processColumns, String selectedBuNo, Map buProcessConfigMap) { + if (rowData == null) { + return ""; + } + // 业务口径:打样状态为“打样完成”时,Tracking Status 固定展示 Complete,不再展示 Delay/On schedule。 + if (isTrackingProofingCompleted(rowData.getProofingStatus())) { + return "Complete"; + } String requiredDeliveryDate = toDateString(rowData.getRequiredDeliveryDate()); if (isBlank(requiredDeliveryDate)) { - return "-"; + return ""; } String today = toDateString(new Date()); String statusPrefix = today.compareTo(requiredDeliveryDate) <= 0 ? "On schedule" : "Delay"; @@ -574,7 +655,7 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic return roleNameValue.toString().trim(); } if (isBlank(roleValue)) { - return "-"; + return ""; } String normalizedRoleValue = roleValue.trim(); int splitIndex = normalizedRoleValue.indexOf('-'); @@ -586,15 +667,15 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic private String getExportDisplayValue(Object value) { if (value == null) { - return "-"; + return ""; } if (value instanceof Date) { // 导出场景下 Date 默认 toString() 会变成 Thu Aug...,统一转 yyyy-MM-dd 便于 Excel 阅读与筛选。 String dateText = toDateString(value); - return isBlank(dateText) ? "-" : dateText; + return isBlank(dateText) ? "" : dateText; } String text = String.valueOf(value); - return isBlank(text) ? "-" : text; + return isBlank(text) ? "" : text; } private String toDateString(Object value) { @@ -2843,6 +2924,53 @@ public class SampleProofTrackingServiceImpl implements SampleProofTrackingServic data.setProofingStatusList(normalizedStatusList.isEmpty() ? null : normalizedStatusList); } + private static class CompletedProcessCellStyleHandler implements CellWriteHandler { + + private final Set completedCellKeySet; + private final Map completedStyleCache = new HashMap<>(); + + private CompletedProcessCellStyleHandler(Set completedCellKeySet) { + this.completedCellKeySet = completedCellKeySet == null ? new LinkedHashSet<>() : completedCellKeySet; + } + + public int order() { + // 晚于默认样式策略执行,避免灰底被后续样式覆盖。 + return Integer.MAX_VALUE; + } + + @Override + public void afterCellDispose(WriteSheetHolder writeSheetHolder, + WriteTableHolder writeTableHolder, + List> cellDataList, + Cell cell, + Head head, + Integer relativeRowIndex, + Boolean isHead) { + if (cell == null || Boolean.TRUE.equals(isHead) || completedCellKeySet.isEmpty()) { + return; + } + long cellKey = buildExportCellCoordinateKey(cell.getRowIndex(), cell.getColumnIndex()); + if (!completedCellKeySet.contains(cellKey)) { + return; + } + CellStyle sourceStyle = cell.getCellStyle(); + Workbook workbook = cell.getSheet().getWorkbook(); + int sourceStyleKey = sourceStyle == null ? -1 : sourceStyle.getIndex(); + CellStyle completedStyle = completedStyleCache.get(sourceStyleKey); + if (completedStyle == null) { + completedStyle = workbook.createCellStyle(); + if (sourceStyle != null) { + completedStyle.cloneStyleFrom(sourceStyle); + } + completedStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + completedStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + completedStyle.setFillBackgroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + completedStyleCache.put(sourceStyleKey, completedStyle); + } + cell.setCellStyle(completedStyle); + } + } + private boolean isBlank(String value) { return value == null || value.trim().isEmpty(); }