diff --git a/bonus-business/src/main/java/com/bonus/business/controller/tool/MonthPlanExcelExporter.java b/bonus-business/src/main/java/com/bonus/business/controller/tool/MonthPlanExcelExporter.java index aa3b5bc..5d268bb 100644 --- a/bonus-business/src/main/java/com/bonus/business/controller/tool/MonthPlanExcelExporter.java +++ b/bonus-business/src/main/java/com/bonus/business/controller/tool/MonthPlanExcelExporter.java @@ -1,44 +1,108 @@ package com.bonus.business.controller.tool; -import com.alibaba.excel.EasyExcel; -import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; import com.bonus.digital.dao.ExportMonthPlanPersonVo; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; +import java.io.OutputStream; import java.net.URLEncoder; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.*; +/** + * 运检人员月度安排导出工具类 + */ public class MonthPlanExcelExporter { private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + // 固定列索引常量 + private static final int COL_SERIAL = 0; // 序号列 + private static final int COL_STATION = 1; // 运检站列 + private static final int COL_NAME = 2; // 姓名列 + private static final int COL_SEX = 3; // 性别列 + private static final int COL_POSITION = 4; // 岗位列 + private static final int COL_NATURE = 5; // 人员性质列 + private static final int COL_CLASSIFY = 6; // 分类列 + private static final int FIRST_DATE_COL = 7;// 第一个日期列(1日)的索引 + + // 列宽兜底值(字符数,避免自适应过窄/过宽) + private static final int MIN_COL_WIDTH = 8; // 最小列宽(字符) + private static final int MAX_COL_WIDTH = 30; // 最大列宽(字符) + /** - * 将原始数据转换为按日期展开的 Map 列表(用于 EasyExcel 导出) - * - * @param dataList 原始数据列表 - * @param year 年份,如 2025 - * @param month 月份,如 10 - * @return List> 用于 EasyExcel writeMap + * 导出运检人员月度安排Excel + * @param response 响应对象 + * @param dataList 人员月度安排数据 + * @param year 年份 + * @param month 月份 + * @param sheetName sheet名称 + * @throws Exception 导出异常 */ - public static List> convertToDailyMapList( + public static void exportToExcel(HttpServletResponse response, + List dataList, + int year, int month, + String sheetName) throws Exception { + // 1. 初始化参数 + 年月合法性校验 + if (year <= 0 || month < 1 || month > 12) { + LocalDate now = LocalDate.now(); + year = now.getYear(); + month = now.getMonthValue(); + } + List finalData = dataList == null ? new ArrayList<>() : dataList; + int daysInMonth = LocalDate.of(year, month, 1).lengthOfMonth(); + String title = year + "年" + month + "月运检人员安排明细"; + + // 2. 创建Workbook和Sheet + Workbook workbook = new XSSFWorkbook(); + Sheet sheet = workbook.createSheet(sheetName); + // 移除错误的 sheet.setWrapText(true); 该方法不存在!自动换行通过 CellStyle 设置 + + // 3. 构建样式 + CellStyle titleStyle = buildTitleStyle(workbook); // 标题样式(含自动换行) + CellStyle headerStyle = buildHeaderStyle(workbook); // 表头样式(含自动换行) + CellStyle contentStyle = buildContentStyle(workbook); // 内容样式(含自动换行) + + // 4. 转换原始数据为行映射(按运检站+姓名分组) + List> rowDataList = convertToDailyMapList(finalData, year, month); + + // 5. 构建表头(标题行 + 列头行) + int headerRowCount = buildHeader(sheet, titleStyle, headerStyle, title, daysInMonth); + + // 6. 填充数据行 + int dataStartRow = headerRowCount; + fillDataRows(sheet, contentStyle, rowDataList, dataStartRow, daysInMonth); + + // 7. 合并日期列相同内容的连续单元格 + mergeSameContentCells(sheet, rowDataList, dataStartRow, daysInMonth); + + // 8. 核心:自适应列宽(替代固定列宽) + autoAdjustColumnWidths(sheet, daysInMonth); + + // 9. 响应输出 + response.reset(); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("UTF-8"); + String fileName = URLEncoder.encode(sheetName, "UTF-8"); + response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx"); + + OutputStream os = response.getOutputStream(); + workbook.write(os); + os.flush(); + os.close(); + workbook.close(); + } + + /** + * 转换原始数据为按行映射的结构(按运检站+姓名分组) + */ + private static List> convertToDailyMapList( List dataList, int year, int month) { - // 获取该月最大天数 int daysInMonth = LocalDate.of(year, month, 1).lengthOfMonth(); - - // 构建表头顺序(固定列 + 动态日期列) - List headers = Arrays.asList( - "运检站", "姓名", "性别", "岗位", "人员性质", "分类" - ); - List dateHeaders = new ArrayList<>(); - for (int d = 1; d <= daysInMonth; d++) { - dateHeaders.add(d + "日"); - } - - // 按 (运检站 + 姓名) 分组 Map> rowMap = new LinkedHashMap<>(); for (ExportMonthPlanPersonVo item : dataList) { @@ -47,37 +111,41 @@ public class MonthPlanExcelExporter { // 初始化行数据 rowMap.computeIfAbsent(key, k -> { Map row = new LinkedHashMap<>(); - row.put("运检站", item.getInspectionStationName()); - row.put("姓名", item.getName()); - row.put("性别", item.getSex()); - row.put("岗位", item.getPositionName()); - row.put("人员性质", item.getPersonnelNatureName()); - row.put("分类", item.getPersonnelClassificationName()); + // 固定列(空值兜底,避免null) + row.put("运检站", item.getInspectionStationName() == null ? "" : item.getInspectionStationName()); + row.put("姓名", item.getName() == null ? "" : item.getName()); + row.put("性别", item.getSex() == null ? "" : item.getSex()); + row.put("岗位", item.getPositionName() == null ? "" : item.getPositionName()); + row.put("人员性质", item.getPersonnelNatureName() == null ? "" : item.getPersonnelNatureName()); + row.put("分类", item.getPersonnelClassificationName() == null ? "" : item.getPersonnelClassificationName()); - // 初始化所有日期列为 "" - for (String dh : dateHeaders) { - row.put(dh, ""); + // 初始化日期列 + Map dailyContent = new HashMap<>(); + for (int d = 1; d <= daysInMonth; d++) { + dailyContent.put(d, ""); } + row.put("dailyContent", dailyContent); return row; }); - // 解析日期范围 + // 填充日期内容(空值校验:避免日期解析失败) + Map dailyContent = (Map) rowMap.get(key).get("dailyContent"); + if (item.getPlannedStartTime() == null || item.getPlannedEndTime() == null) { + continue; + } LocalDate start = LocalDate.parse(item.getPlannedStartTime(), DATE_FORMATTER); LocalDate end = LocalDate.parse(item.getPlannedEndTime(), DATE_FORMATTER); - // 遍历每一天 for (LocalDate date = start; !date.isAfter(end); date = date.plusDays(1)) { if (date.getYear() == year && date.getMonthValue() == month) { int day = date.getDayOfMonth(); - String dateKey = day + "日"; - String currentContent = (String) rowMap.get(key).get(dateKey); - String newContent = item.getWorkContent(); + String currentContent = dailyContent.get(day); + String newContent = item.getWorkContent() == null ? "" : item.getWorkContent(); - // 如果当天已有内容,用换行拼接 if (currentContent == null || currentContent.trim().isEmpty()) { - rowMap.get(key).put(dateKey, newContent); + dailyContent.put(day, newContent); } else { - rowMap.get(key).put(dateKey, currentContent + "\n" + newContent); + dailyContent.put(day, currentContent + "\n" + newContent); } } } @@ -87,26 +155,258 @@ public class MonthPlanExcelExporter { } /** - * 导出 Excel 到 HttpServletResponse(Web 场景) + * 构建标题样式(含自动换行 + 居中) */ - public static void exportToExcel(HttpServletResponse response, - List dataList, - int year, int month, - String sheetName) throws IOException { + private static CellStyle buildTitleStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + // 字体配置 + Font font = workbook.createFont(); + font.setBold(true); + font.setFontName("宋体"); + font.setFontHeightInPoints((short) 16); + style.setFont(font); + // 对齐方式(垂直+水平居中) + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + // 边框 + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.BLACK.getIndex()); + style.setBottomBorderColor(IndexedColors.BLACK.getIndex()); + style.setLeftBorderColor(IndexedColors.BLACK.getIndex()); + style.setRightBorderColor(IndexedColors.BLACK.getIndex()); + // 核心:单元格自动换行(解决长内容遮挡) + style.setWrapText(true); + return style; + } - List> mapList = convertToDailyMapList(dataList, year, month); + /** + * 构建表头样式(含自动换行 + 居中) + */ + private static CellStyle buildHeaderStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + Font font = workbook.createFont(); + font.setBold(true); + font.setFontName("宋体"); + font.setFontHeightInPoints((short) 12); + style.setFont(font); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.BLACK.getIndex()); + style.setBottomBorderColor(IndexedColors.BLACK.getIndex()); + style.setLeftBorderColor(IndexedColors.BLACK.getIndex()); + style.setRightBorderColor(IndexedColors.BLACK.getIndex()); + style.setWrapText(true); // 自动换行 + return style; + } - // 设置响应头 - response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); - response.setCharacterEncoding("utf-8"); - String fileName = URLEncoder.encode(sheetName, "UTF-8"); - response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"); + /** + * 构建内容样式(含自动换行 + 垂直居中) + */ + private static CellStyle buildContentStyle(Workbook workbook) { + CellStyle style = workbook.createCellStyle(); + Font font = workbook.createFont(); + font.setFontName("宋体"); + font.setFontHeightInPoints((short) 11); + style.setFont(font); + // 水平+垂直居中(解决内容上下/左右遮挡) + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderTop(BorderStyle.THIN); + style.setBorderBottom(BorderStyle.THIN); + style.setBorderLeft(BorderStyle.THIN); + style.setBorderRight(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.BLACK.getIndex()); + style.setBottomBorderColor(IndexedColors.BLACK.getIndex()); + style.setLeftBorderColor(IndexedColors.BLACK.getIndex()); + style.setRightBorderColor(IndexedColors.BLACK.getIndex()); + style.setWrapText(true); // 自动换行 + return style; + } - // 使用 EasyExcel 导出 - EasyExcel.write(response.getOutputStream()) - .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) - .autoCloseStream(true) - .sheet(sheetName) - .doWrite(mapList); + /** + * 构建表头(标题行 + 列头行) + * @return 表头总行数 + */ + private static int buildHeader(Sheet sheet, CellStyle titleStyle, CellStyle headerStyle, + String title, int daysInMonth) { + // 计算总列数:固定列(7列) + 日期列 + int totalCols = FIRST_DATE_COL + daysInMonth; + + // 行0:标题行(合并所有列) + Row titleRow = sheet.createRow(0); + titleRow.setHeightInPoints(40); // 加高标题行(适配换行) + Cell titleCell = titleRow.createCell(0); + titleCell.setCellValue(title); + titleCell.setCellStyle(titleStyle); + sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, totalCols - 1)); + + // 行1:列头行 + Row headerRow = sheet.createRow(1); + headerRow.setHeightInPoints(30); // 加高表头行 + // 固定列头 + createCell(headerRow, COL_SERIAL, "序号", headerStyle); + createCell(headerRow, COL_STATION, "运检站", headerStyle); + createCell(headerRow, COL_NAME, "姓名", headerStyle); + createCell(headerRow, COL_SEX, "性别", headerStyle); + createCell(headerRow, COL_POSITION, "岗位", headerStyle); + createCell(headerRow, COL_NATURE, "人员性质", headerStyle); + createCell(headerRow, COL_CLASSIFY, "分类", headerStyle); + // 日期列头 + for (int d = 1; d <= daysInMonth; d++) { + int colIndex = FIRST_DATE_COL + (d - 1); + createCell(headerRow, colIndex, d + "日", headerStyle); + } + + return 2; // 表头总行数:标题行(0) + 列头行(1) + } + + /** + * 填充数据行(加高行高,适配自动换行) + */ + private static void fillDataRows(Sheet sheet, CellStyle contentStyle, + List> rowDataList, + int startRowNum, int daysInMonth) { + int serialNum = 1; // 序号 + + for (int i = 0; i < rowDataList.size(); i++) { + int rowNum = startRowNum + i; + Row row = sheet.createRow(rowNum); + row.setHeightInPoints(35); // 加高数据行(解决换行内容遮挡) + Map rowData = rowDataList.get(i); + + // 填充固定列(空值兜底) + createCell(row, COL_SERIAL, serialNum++, contentStyle); + createCell(row, COL_STATION, rowData.get("运检站"), contentStyle); + createCell(row, COL_NAME, rowData.get("姓名"), contentStyle); + createCell(row, COL_SEX, rowData.get("性别"), contentStyle); + createCell(row, COL_POSITION, rowData.get("岗位"), contentStyle); + createCell(row, COL_NATURE, rowData.get("人员性质"), contentStyle); + createCell(row, COL_CLASSIFY, rowData.get("分类"), contentStyle); + + // 填充日期列 + Map dailyContent = (Map) rowData.get("dailyContent"); + for (int d = 1; d <= daysInMonth; d++) { + int colIndex = FIRST_DATE_COL + (d - 1); + String content = dailyContent.get(d) == null ? "" : dailyContent.get(d); + createCell(row, colIndex, content, contentStyle); + } + } + } + + /** + * 合并日期列中相同内容的连续单元格 + */ + private static void mergeSameContentCells(Sheet sheet, List> rowDataList, + int dataStartRow, int daysInMonth) { + // 遍历每一行 + for (int rowIdx = 0; rowIdx < rowDataList.size(); rowIdx++) { + int currentRowNum = dataStartRow + rowIdx; + Map rowData = rowDataList.get(rowIdx); + Map dailyContent = (Map) rowData.get("dailyContent"); + + // 遍历日期列,查找连续相同内容的单元格 + for (int d = 1; d <= daysInMonth; ) { + String baseContent = dailyContent.get(d) == null ? "" : dailyContent.get(d); + if (baseContent.trim().isEmpty()) { + d++; + continue; + } + + // 查找连续相同内容的结束日期 + int endDay = d; + while (endDay + 1 <= daysInMonth) { + String nextContent = dailyContent.get(endDay + 1) == null ? "" : dailyContent.get(endDay + 1); + if (baseContent.equals(nextContent)) { + endDay++; + } else { + break; + } + } + + // 若有连续相同内容,合并单元格 + if (endDay > d) { + int startCol = FIRST_DATE_COL + (d - 1); + int endCol = FIRST_DATE_COL + (endDay - 1); + sheet.addMergedRegion(new CellRangeAddress( + currentRowNum, currentRowNum, // 同一行 + startCol, endCol // 起始列-结束列 + )); + // 跳过已合并的列 + d = endDay + 1; + } else { + d++; + } + } + } + } + + /** + * 核心:自适应列宽(替代固定列宽) + * 1. 自动计算每列的最大内容宽度 + * 2. 限制最小/最大宽度,避免过窄/过宽 + * 3. 兼容合并单元格的列宽计算 + */ + private static void autoAdjustColumnWidths(Sheet sheet, int daysInMonth) { + // 计算总列数:固定列(7列) + 日期列 + int totalCols = FIRST_DATE_COL + daysInMonth; + + // 遍历所有列,自适应宽度 + for (int colIndex = 0; colIndex < totalCols; colIndex++) { + // 第一步:自动计算列宽(POI原生方法,基于列中最长内容) + // true:忽略合并单元格,仅计算真实内容(避免合并单元格导致列宽计算异常) + sheet.autoSizeColumn(colIndex, true); + + // 第二步:转换列宽单位(POI的列宽单位是1/256个字符宽度) + int currentWidth = sheet.getColumnWidth(colIndex) / 256; + + // 第三步:限制最小/最大宽度(兜底) + int targetWidth; + if (currentWidth < MIN_COL_WIDTH) { + targetWidth = MIN_COL_WIDTH; + } else if (currentWidth > MAX_COL_WIDTH) { + targetWidth = MAX_COL_WIDTH; + } else { + targetWidth = currentWidth; + } + + // 第四步:设置最终列宽(加2个字符余量,避免中文内容紧贴边框) + sheet.setColumnWidth(colIndex, (targetWidth + 2) * 256); + } + + // 特殊优化:序号列强制最小宽度(避免过窄) + sheet.setColumnWidth(COL_SERIAL, (MIN_COL_WIDTH + 1) * 256); + // 特殊优化:运检站列放宽最大宽度(适配长名称) + int stationWidth = sheet.getColumnWidth(COL_STATION) / 256; + if (stationWidth > MAX_COL_WIDTH) { + sheet.setColumnWidth(COL_STATION, (MAX_COL_WIDTH + 5) * 256); + } + } + + /** + * 创建单元格并设置值和样式(空值兜底) + */ + private static void createCell(Row row, int colIndex, Object value, CellStyle style) { + Cell cell = row.createCell(colIndex); + // 空值兜底,避免null导致单元格无内容 + Object cellValue = value == null ? "" : value; + if (cellValue instanceof String) { + cell.setCellValue((String) cellValue); + } else if (cellValue instanceof Integer) { + cell.setCellValue((Integer) cellValue); + } else if (cellValue instanceof Long) { + cell.setCellValue((Long) cellValue); + } else if (cellValue instanceof Double) { + cell.setCellValue((Double) cellValue); + } else { + cell.setCellValue(cellValue.toString()); + } + cell.setCellStyle(style); } } diff --git a/bonus-business/src/main/java/com/bonus/business/controller/tool/WorkloadAndCarSummaryExcelExporter.java b/bonus-business/src/main/java/com/bonus/business/controller/tool/WorkloadAndCarSummaryExcelExporter.java index bc088b9..102c82a 100644 --- a/bonus-business/src/main/java/com/bonus/business/controller/tool/WorkloadAndCarSummaryExcelExporter.java +++ b/bonus-business/src/main/java/com/bonus/business/controller/tool/WorkloadAndCarSummaryExcelExporter.java @@ -11,7 +11,6 @@ import java.io.OutputStream; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * 工作量+用车清单导出工具类 @@ -19,6 +18,8 @@ import java.util.Objects; * 1. 标题移除年份前缀(仅保留月份) * 2. 移除所有样式的灰色背景色 * 3. 修复工作量/用车标题边框缺失问题(为合并区域所有单元格应用样式+显式设置边框颜色) + * 4. 修复用车汇总合计行J列(管理用车天数)合计值缺失问题(调整合并区域) + * 5. 用车表格序号为0的行:序号、管理用车天数、分包用车天数置为空 */ public class WorkloadAndCarSummaryExcelExporter { @@ -226,6 +227,7 @@ public class WorkloadAndCarSummaryExcelExporter { /** * 填充数据行(从行2开始) + * 核心修改:用车数据行序号为0时,序号、管理用车天数、分包用车天数置为空 */ private static void fillDataRows(Sheet sheet, CellStyle contentStyle, List workloadData, @@ -244,12 +246,29 @@ public class WorkloadAndCarSummaryExcelExporter { createCell(row, COL_E, workloadVo.getUnitPrice() == null ? 0 : workloadVo.getUnitPrice(), contentStyle); createCell(row, COL_F, workloadVo.getTotalAmount() == null ? 0 : workloadVo.getTotalAmount(), contentStyle); - // 填充用车数据 + // 填充用车数据(核心修改:序号为0时置空) CarUseSummaryExcelVo carVo = i < carData.size() ? carData.get(i) : new CarUseSummaryExcelVo(); - createCell(row, COL_H, carVo.getSerialNumber() == null ? 0 : carVo.getSerialNumber(), contentStyle); + Integer carSerialNum = carVo.getSerialNumber(); + // 序号:为0或null时置空,否则填实际值 + if (carSerialNum == null || carSerialNum == 0) { + createCell(row, COL_H, "", contentStyle); + } else { + createCell(row, COL_H, carSerialNum, contentStyle); + } + // 运检站:保留原有逻辑 createCell(row, COL_I, carVo.getInspectionStationName() == null ? "" : carVo.getInspectionStationName(), contentStyle); - createCell(row, COL_J, carVo.getTotalManageCarDays() == null ? 0 : carVo.getTotalManageCarDays(), contentStyle); - createCell(row, COL_K, carVo.getTotalSubCarDays() == null ? 0 : carVo.getTotalSubCarDays(), contentStyle); + // 管理用车天数:序号为0/null时置空,否则填实际值(无值则0) + if (carSerialNum == null || carSerialNum == 0) { + createCell(row, COL_J, "", contentStyle); + } else { + createCell(row, COL_J, carVo.getTotalManageCarDays() == null ? 0 : carVo.getTotalManageCarDays(), contentStyle); + } + // 分包用车天数:序号为0/null时置空,否则填实际值(无值则0) + if (carSerialNum == null || carSerialNum == 0) { + createCell(row, COL_K, "", contentStyle); + } else { + createCell(row, COL_K, carVo.getTotalSubCarDays() == null ? 0 : carVo.getTotalSubCarDays(), contentStyle); + } } } @@ -278,14 +297,19 @@ public class WorkloadAndCarSummaryExcelExporter { int subCarTotal = 0; for (CarUseSummaryExcelVo vo : carData) { if (vo != null) { - if (vo.getTotalManageCarDays() != null) { - manageCarTotal += vo.getTotalManageCarDays(); - } - if (vo.getTotalSubCarDays() != null) { - subCarTotal += vo.getTotalSubCarDays(); + // 仅统计序号非0的有效数据 + Integer serialNum = vo.getSerialNumber(); + if (serialNum != null && serialNum != 0) { + if (vo.getTotalManageCarDays() != null) { + manageCarTotal += vo.getTotalManageCarDays(); + } + if (vo.getTotalSubCarDays() != null) { + subCarTotal += vo.getTotalSubCarDays(); + } } } } + // 确保J列(管理用车天数合计)和K列(分包用车天数合计)正常赋值 createCell(totalRow, COL_J, manageCarTotal, totalStyle); createCell(totalRow, COL_K, subCarTotal, totalStyle); @@ -298,7 +322,7 @@ public class WorkloadAndCarSummaryExcelExporter { } /** - * 设置合并区域 + * 设置合并区域(核心修改:调整用车合计行的合并范围,避免覆盖J列合计值) */ private static void setMergedRegions(Sheet sheet, int totalRowNum) { // 一级标题合并 @@ -306,7 +330,8 @@ public class WorkloadAndCarSummaryExcelExporter { sheet.addMergedRegion(new CellRangeAddress(0, 0, COL_H, COL_K)); // 合计行合并 sheet.addMergedRegion(new CellRangeAddress(totalRowNum, totalRowNum, COL_A, COL_E)); - sheet.addMergedRegion(new CellRangeAddress(totalRowNum, totalRowNum, COL_H, COL_J)); + // 核心修改:用车合计行仅合并H-I列,不再合并J列,确保J列和K列的合计值正常显示 + sheet.addMergedRegion(new CellRangeAddress(totalRowNum, totalRowNum, COL_H, COL_I)); } /** diff --git a/bonus-business/src/main/java/com/bonus/digital/controller/MonthlyPlanController.java b/bonus-business/src/main/java/com/bonus/digital/controller/MonthlyPlanController.java index 0b5d195..c73308d 100644 --- a/bonus-business/src/main/java/com/bonus/digital/controller/MonthlyPlanController.java +++ b/bonus-business/src/main/java/com/bonus/digital/controller/MonthlyPlanController.java @@ -66,7 +66,7 @@ public class MonthlyPlanController extends BaseController { */ @PreAuthorize("@ss.hasPermi('monthly:plan:add')") @PostMapping("/addMonthlyPlan") - public AjaxResult addMonthlyPlan(@RequestBody List monthlyPlanVo) { + public AjaxResult addMonthlyPlan(@RequestBody MonthlyPlanVo monthlyPlanVo) { try { int res = monthlyPlanService.addMonthlyPlanList(monthlyPlanVo); if (res > 0) { @@ -118,11 +118,25 @@ public class MonthlyPlanController extends BaseController { @Log(title = "导出人员安排表", businessType = BusinessType.EXPORT) @PreAuthorize("@ss.hasPermi('monthly:plan:export')") @PostMapping("/export") - public void export(HttpServletResponse response, MonthlyPlanVo monthlyPlanVo) throws IOException { + public void export(HttpServletResponse response, @RequestBody MonthlyPlanVo monthlyPlanVo) throws Exception { List list = monthlyPlanService.exportMonthlyPlanPerson(monthlyPlanVo); - int year = LocalDate.now().getYear(); - int month = LocalDate.now().getMonthValue(); - MonthPlanExcelExporter.exportToExcel(response, list, year, month, month+"月运检人员安排"); + + // 动态解析年月,添加兜底逻辑 + int year = 0; + int month = 0; + String monthlyPlan = monthlyPlanVo.getMonthlyPlan(); + + if (monthlyPlan != null && monthlyPlan.matches("\\d{4}-\\d{1,2}")) { // 校验格式:yyyy-MM + String[] yearMonthArr = monthlyPlan.split("-"); + year = Integer.parseInt(yearMonthArr[0]); + month = Integer.parseInt(yearMonthArr[1]); + } + + // 动态拼接sheet名称 + String sheetName = month + "月运检人员安排"; + + // 传入动态年月 + MonthPlanExcelExporter.exportToExcel(response, list, year, month, sheetName); } @Log(title = "导出工作量汇总表", businessType = BusinessType.EXPORT) diff --git a/bonus-business/src/main/resources/mapper/DayPlanMapper.xml b/bonus-business/src/main/resources/mapper/DayPlanMapper.xml index 0e89670..9712688 100644 --- a/bonus-business/src/main/resources/mapper/DayPlanMapper.xml +++ b/bonus-business/src/main/resources/mapper/DayPlanMapper.xml @@ -226,12 +226,8 @@ left join tb_plan_management pm on pm.plan_management_id = tmp.plan_management_id where tmp.is_active = '1' - - - AND tdp.day_plan >= #{startTime} - - - AND tdp.day_plan <= #{endTime} + + AND DATE_FORMAT(tdp.day_plan, '%Y-%m-%d') BETWEEN #{startTime} AND #{endTime} GROUP BY tmp.inspection_station_id, diff --git a/bonus-business/src/main/resources/mapper/MonthPlanMapper.xml b/bonus-business/src/main/resources/mapper/MonthPlanMapper.xml index 3477604..93a9235 100644 --- a/bonus-business/src/main/resources/mapper/MonthPlanMapper.xml +++ b/bonus-business/src/main/resources/mapper/MonthPlanMapper.xml @@ -193,27 +193,6 @@ ORDER BY tmp.inspection_station_name ASC; -