From bb59628452a0e207bc750091b4f5b2eee3ba38f1 Mon Sep 17 00:00:00 2001 From: xs Date: Wed, 31 Dec 2025 16:27:01 +0800 Subject: [PATCH] =?UTF-8?q?1.2.01=E5=90=8E=E7=AB=AF=EF=BC=9A=20=20=20=20fe?= =?UTF-8?q?at(budget):=E6=A0=B9=E6=8D=AE=E7=A0=94=E5=8F=91=E9=A2=84?= =?UTF-8?q?=E7=AE=97=E6=96=B0=E6=A8=A1=E6=9D=BF=E4=BF=AE=E6=94=B9=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/oa/erp/enums/TechTypeEnum.java | 4 +- .../dromara/oa/erp/enums/TripTypeEnum.java | 51 + .../dromara/oa/excel/BaseExcelExporter.java | 635 +++- .../dromara/oa/excel/ExcelStyleBuilder.java | 309 ++ .../MarketProjectBudgetExcelExporter.java | 4 +- .../dromara/oa/excel/RdBudgetItemEnums.java | 55 + .../excel/RdProjectBudgetExcelExporter.java | 2876 +++++++---------- 7 files changed, 2225 insertions(+), 1709 deletions(-) create mode 100644 ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/enums/TripTypeEnum.java create mode 100644 ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/ExcelStyleBuilder.java create mode 100644 ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/RdBudgetItemEnums.java diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/enums/TechTypeEnum.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/enums/TechTypeEnum.java index 0bf8cd7b..3c0e4591 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/enums/TechTypeEnum.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/enums/TechTypeEnum.java @@ -10,9 +10,9 @@ import lombok.Getter; @Getter public enum TechTypeEnum { /** - * 技术咨询开发 + * 新产品设计费 */ - TECH_CONSULT("1", "技术咨询开发"), + TECH_CONSULT("1", "新产品设计费"), /** * 专家咨询-会议形式 diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/enums/TripTypeEnum.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/enums/TripTypeEnum.java new file mode 100644 index 00000000..b59bf2af --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/enums/TripTypeEnum.java @@ -0,0 +1,51 @@ +package org.dromara.oa.erp.enums; + +import lombok.Getter; + +/** + * @author xins + * @description erp_rd_budget_travel_cost类型枚举 + * @date 2025/12/11 13:58 + */ +@Getter +public enum TripTypeEnum { + /** + * 差旅费 + */ + TRAVEL("1", "差旅费"), + + /** + * 交通费 + */ + TRANSPORTATION("2", "交通费"); + + + /** + * 编码 + */ + private final String code; + + /** + * 名称 + */ + private final String name; + + + TripTypeEnum(String code, String name) { + this.code = code; + this.name = name; + } + + /** + * 根据流程编码获取配置 + */ + public static TripTypeEnum getByCode(String code) { + for (TripTypeEnum config : values()) { + if (config.getCode().equals(code)) { + return config; + } + } + return null; + } + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/BaseExcelExporter.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/BaseExcelExporter.java index c7cd5c2d..f145f0d1 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/BaseExcelExporter.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/BaseExcelExporter.java @@ -10,6 +10,8 @@ import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -24,6 +26,10 @@ public abstract class BaseExcelExporter { protected Workbook workbook; + //字体定义 + protected Font yaheiNormal10Font; + protected Font yaheiBold10Font; + // 样式定义 protected CellStyle titleStyle; protected CellStyle formLeftStyle; @@ -40,9 +46,54 @@ public abstract class BaseExcelExporter { protected CellStyle remarkStyle; protected CellStyle leftMergeStyle; + protected CellStyle rdCoverTitleStyle; + protected CellStyle rdCoverSubtitleStyle; + protected CellStyle rdCoverFormKeyStyle; + protected CellStyle rdCoverFormValueStyle; + + protected CellStyle rdTitleStyle; + protected CellStyle rdHeaderStyle; + protected CellStyle rdNoTopBorderHeaderStyle; + protected CellStyle rdNoBottomBorderHeaderStyle; + protected CellStyle rdLeftHeaderStyle; + protected CellStyle rdRightHeaderStyle; + protected CellStyle rdFooterStyle; + protected CellStyle rdLeftFooterStyle; + protected CellStyle rdRightFooterStyle; + + protected CellStyle rdDataStyle; + protected CellStyle rdLeftDataStyle; + protected CellStyle rdRightDataStyle; + protected CellStyle rdLeftBottomDataStyle; + protected CellStyle rdRightBottomDataStyle; + + + protected CellStyle rdDataBoldStyle; + protected CellStyle rdLeftDataBoldStyle; + protected CellStyle rdRightDataBoldStyle; + protected CellStyle rdDataRedBoldStyle; + protected CellStyle rdLeftDataRedBoldStyle; + protected CellStyle rdRightDataRedBoldStyle; + protected CellStyle rdBottomDataRedBoldStyle; + protected CellStyle rdFormulaStyle; + protected CellStyle rdRightFormulaStyle; + protected CellStyle rdFooterFormulaStyle; + protected CellStyle rdFormulaPercentageStyle; + protected CellStyle rdFooterFormulaPercentageStyle; + protected CellStyle rdFooterRightFormulaStyle; + protected CellStyle rdInstructionStyle; + protected CellStyle rdFormDataStyle; + protected CellStyle rdFormStyle; + protected CellStyle rdFormFormulaStyle; + + + // 存储各sheet的总计行位置 protected final Map sheetTotalRowMap = new ConcurrentHashMap<>(); + // 富文本样式缓存 + protected final Map fontCache = new ConcurrentHashMap<>(); + /** * 导出Excel到字节数组 */ @@ -64,6 +115,9 @@ public abstract class BaseExcelExporter { * 创建所有样式 */ protected void createStyles() { + //创建字体样式 + createFont(); + // 创建标题样式 createTitleStyle(); @@ -78,8 +132,69 @@ public abstract class BaseExcelExporter { // 创建其他样式 createOtherStyles(); + + // 创建研发预算封面样式 + createRdCoverStyle(); } + /** + * 创建字体样式 + */ + protected void createFont() { + yaheiNormal10Font = workbook.createFont(); + yaheiNormal10Font.setFontName("微软雅黑"); + yaheiNormal10Font.setFontHeightInPoints((short) 10); + + yaheiBold10Font = workbook.createFont(); + yaheiBold10Font.setBold(true); + yaheiBold10Font.setFontName("微软雅黑"); + yaheiBold10Font.setFontHeightInPoints((short) 10); + + initFontCache(); + } + + /** + * 初始化字体缓存 + */ + protected void initFontCache() { + fontCache.clear(); + fontCache.put("normal-10", yaheiNormal10Font); + fontCache.put("bold-10", yaheiBold10Font); + } + + /** + * 获取或创建字体(带缓存) + */ + protected Font getOrCreateFont(String fontKey, FontStyleConfig config) { + if (fontCache.containsKey(fontKey)) { + return fontCache.get(fontKey); + } + + Font font = workbook.createFont(); + font.setFontName(config.getFontName() != null ? config.getFontName() : "微软雅黑"); + font.setFontHeightInPoints(config.getFontSize()); + + if (config.isBold()) { + font.setBold(true); + } + + if (config.getColor() != null) { + font.setColor(config.getColor()); + } + + if (config.isItalic()) { + font.setItalic(true); + } + + if (config.isUnderline()) { + font.setUnderline(Font.U_SINGLE); + } + + fontCache.put(fontKey, font); + return font; + } + + /** * 创建标题样式 */ @@ -95,6 +210,275 @@ public abstract class BaseExcelExporter { setBorder(titleStyle, BorderStyle.MEDIUM); } + /** + * 创建标题样式 + */ + protected void createRdCoverStyle() { + rdCoverTitleStyle = workbook.createCellStyle(); + Font titleFont = workbook.createFont(); + titleFont.setBold(true); + titleFont.setFontName("宋体"); + titleFont.setFontHeightInPoints((short) 26); + rdCoverTitleStyle.setFont(titleFont); + rdCoverTitleStyle.setAlignment(HorizontalAlignment.CENTER); + rdCoverTitleStyle.setVerticalAlignment(VerticalAlignment.CENTER); + + rdCoverSubtitleStyle = workbook.createCellStyle(); + Font rdSubtitleFont = workbook.createFont(); + rdSubtitleFont.setBold(true); + rdSubtitleFont.setFontName("宋体"); + rdSubtitleFont.setFontHeightInPoints((short) 48); + rdCoverSubtitleStyle.setFont(rdSubtitleFont); + rdCoverSubtitleStyle.setAlignment(HorizontalAlignment.CENTER); + rdCoverSubtitleStyle.setVerticalAlignment(VerticalAlignment.CENTER); + + Font formFont = workbook.createFont(); + formFont.setFontName("宋体"); + formFont.setFontHeightInPoints((short) 18); + + rdCoverFormKeyStyle = workbook.createCellStyle(); + rdCoverFormKeyStyle.setFont(formFont); + rdCoverFormKeyStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdCoverFormKeyStyle.setAlignment(HorizontalAlignment.LEFT); + + rdCoverFormValueStyle = workbook.createCellStyle(); + rdCoverFormValueStyle.setFont(formFont); + rdCoverFormValueStyle.setBorderBottom(BorderStyle.THIN); + rdCoverFormValueStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex()); + rdCoverFormValueStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdCoverFormValueStyle.setAlignment(HorizontalAlignment.CENTER); + + // 研发预算表头样式 + rdTitleStyle = workbook.createCellStyle(); + Font rdTitleFont = workbook.createFont(); + rdTitleFont.setBold(true); + rdTitleFont.setFontName("微软雅黑"); + rdTitleFont.setFontHeightInPoints((short) 18); + rdTitleStyle.setFont(rdTitleFont); + rdTitleStyle.setAlignment(HorizontalAlignment.CENTER); + rdTitleStyle.setVerticalAlignment(VerticalAlignment.CENTER); + + + + rdHeaderStyle = workbook.createCellStyle(); + rdHeaderStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + rdHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font dataBoldFont = workbook.createFont(); + dataBoldFont.setFontName("微软雅黑"); + dataBoldFont.setFontHeightInPoints((short) 11); + dataBoldFont.setBold(true); + rdHeaderStyle.setFont(dataBoldFont); + setBorder(rdHeaderStyle, BorderStyle.DOUBLE, BorderStyle.THIN, BorderStyle.THIN, BorderStyle.THIN); + rdHeaderStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdHeaderStyle.setAlignment(HorizontalAlignment.CENTER); + + rdLeftHeaderStyle = workbook.createCellStyle(); + rdLeftHeaderStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + rdLeftHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + rdLeftHeaderStyle.setFont(dataBoldFont); + setBorder(rdLeftHeaderStyle, BorderStyle.DOUBLE, BorderStyle.THIN, BorderStyle.DOUBLE, BorderStyle.THIN); + rdLeftHeaderStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdLeftHeaderStyle.setAlignment(HorizontalAlignment.CENTER); + + rdRightHeaderStyle = workbook.createCellStyle(); + rdRightHeaderStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + rdRightHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + rdRightHeaderStyle.setFont(dataBoldFont); + setBorder(rdRightHeaderStyle, BorderStyle.DOUBLE, BorderStyle.THIN, BorderStyle.THIN, BorderStyle.DOUBLE); + rdRightHeaderStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdRightHeaderStyle.setAlignment(HorizontalAlignment.CENTER); + + + rdNoTopBorderHeaderStyle = workbook.createCellStyle(); + rdNoTopBorderHeaderStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + rdNoTopBorderHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + rdNoTopBorderHeaderStyle.setFont(dataBoldFont); + setBorder(rdNoTopBorderHeaderStyle, null, BorderStyle.THIN, BorderStyle.THIN, BorderStyle.THIN); + rdNoTopBorderHeaderStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdNoTopBorderHeaderStyle.setAlignment(HorizontalAlignment.CENTER); + + rdNoBottomBorderHeaderStyle = workbook.createCellStyle(); + rdNoBottomBorderHeaderStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + rdNoBottomBorderHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + rdNoBottomBorderHeaderStyle.setFont(dataBoldFont); + setBorder(rdNoBottomBorderHeaderStyle, BorderStyle.DOUBLE, null, BorderStyle.THIN, BorderStyle.THIN); + rdNoBottomBorderHeaderStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdNoBottomBorderHeaderStyle.setAlignment(HorizontalAlignment.CENTER); + + + Font dataFont = workbook.createFont(); + dataFont.setFontName("微软雅黑"); + dataFont.setFontHeightInPoints((short) 11); + + rdDataStyle = workbook.createCellStyle(); + rdDataStyle.setFont(dataFont); + setBorder(rdDataStyle, BorderStyle.THIN); + rdDataStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdDataStyle.setAlignment(HorizontalAlignment.CENTER); + rdDataStyle.setWrapText(true); + + rdLeftDataStyle = workbook.createCellStyle(); + rdLeftDataStyle.setFont(dataFont); + setBorder(rdLeftDataStyle, BorderStyle.THIN, BorderStyle.THIN, BorderStyle.DOUBLE, BorderStyle.THIN); + rdLeftDataStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdLeftDataStyle.setAlignment(HorizontalAlignment.CENTER); + + rdRightDataStyle = workbook.createCellStyle(); + rdRightDataStyle.setFont(dataFont); + setBorder(rdRightDataStyle, BorderStyle.THIN, BorderStyle.THIN, BorderStyle.THIN, BorderStyle.DOUBLE); + rdRightDataStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdRightDataStyle.setAlignment(HorizontalAlignment.CENTER); + rdRightDataStyle.setWrapText(true); + + rdLeftBottomDataStyle = workbook.createCellStyle(); + rdLeftBottomDataStyle.setFont(dataFont); + setBorder(rdLeftBottomDataStyle, BorderStyle.THIN, BorderStyle.DOUBLE, BorderStyle.DOUBLE, BorderStyle.THIN); + rdLeftBottomDataStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdLeftBottomDataStyle.setAlignment(HorizontalAlignment.CENTER); + + rdRightBottomDataStyle = workbook.createCellStyle(); + rdRightBottomDataStyle.setFont(dataFont); + setBorder(rdRightBottomDataStyle, BorderStyle.THIN, BorderStyle.DOUBLE, BorderStyle.THIN, BorderStyle.DOUBLE); + rdRightBottomDataStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdRightBottomDataStyle.setAlignment(HorizontalAlignment.CENTER); + rdRightBottomDataStyle.setWrapText(true); + + + rdDataBoldStyle = workbook.createCellStyle(); + rdDataBoldStyle.setFont(dataBoldFont); + setBorder(rdDataBoldStyle, BorderStyle.THIN); + rdDataBoldStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdDataBoldStyle.setAlignment(HorizontalAlignment.CENTER); + rdDataBoldStyle.setWrapText(true); + + rdLeftDataBoldStyle = workbook.createCellStyle(); + rdLeftDataBoldStyle.setFont(dataBoldFont); + setBorder(rdLeftDataBoldStyle, BorderStyle.DOUBLE, BorderStyle.THIN, BorderStyle.THIN, BorderStyle.DOUBLE); + rdLeftDataBoldStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdLeftDataBoldStyle.setAlignment(HorizontalAlignment.CENTER); + + rdRightDataBoldStyle = workbook.createCellStyle(); + rdRightDataBoldStyle.setFont(dataBoldFont); + setBorder(rdRightDataBoldStyle, BorderStyle.DOUBLE, BorderStyle.THIN, BorderStyle.DOUBLE, BorderStyle.THIN); + rdRightDataBoldStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdRightDataBoldStyle.setAlignment(HorizontalAlignment.CENTER); + rdRightDataBoldStyle.setWrapText(true); + + Font dataRedBoldFont = workbook.createFont(); + dataRedBoldFont.setFontName("微软雅黑"); + dataRedBoldFont.setFontHeightInPoints((short) 11); + dataRedBoldFont.setBold(true); + dataRedBoldFont.setColor(IndexedColors.RED.getIndex()); + + rdDataRedBoldStyle = workbook.createCellStyle(); + rdDataRedBoldStyle.setFont(dataRedBoldFont); + setBorder(rdDataRedBoldStyle, BorderStyle.THIN, BorderStyle.THIN, BorderStyle.THIN, BorderStyle.THIN); + rdDataRedBoldStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdDataRedBoldStyle.setAlignment(HorizontalAlignment.CENTER); + + rdLeftDataRedBoldStyle = workbook.createCellStyle(); + rdLeftDataRedBoldStyle.setFont(dataRedBoldFont); + setBorder(rdLeftDataRedBoldStyle, BorderStyle.DOUBLE, BorderStyle.THIN, BorderStyle.DOUBLE, BorderStyle.THIN); + rdLeftDataRedBoldStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdLeftDataRedBoldStyle.setAlignment(HorizontalAlignment.CENTER); + + rdRightDataRedBoldStyle = workbook.createCellStyle(); + rdRightDataRedBoldStyle.setFont(dataRedBoldFont); + setBorder(rdRightDataRedBoldStyle, BorderStyle.DOUBLE, BorderStyle.THIN, BorderStyle.THIN, BorderStyle.DOUBLE); + rdRightDataRedBoldStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdRightDataRedBoldStyle.setAlignment(HorizontalAlignment.CENTER); + + rdBottomDataRedBoldStyle = workbook.createCellStyle(); + rdBottomDataRedBoldStyle.setFont(dataRedBoldFont); + setBorder(rdBottomDataRedBoldStyle, BorderStyle.THIN, BorderStyle.DOUBLE, BorderStyle.THIN, BorderStyle.THIN); + rdBottomDataRedBoldStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdBottomDataRedBoldStyle.setAlignment(HorizontalAlignment.CENTER); + + + rdFooterStyle = workbook.createCellStyle(); + rdFooterStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + rdFooterStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + rdFooterStyle.setFont(dataBoldFont); + setBorder(rdFooterStyle, BorderStyle.THIN, BorderStyle.DOUBLE, BorderStyle.THIN, BorderStyle.THIN); + rdFooterStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdFooterStyle.setAlignment(HorizontalAlignment.CENTER); + + rdLeftFooterStyle = workbook.createCellStyle(); + rdLeftFooterStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + rdLeftFooterStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + rdLeftFooterStyle.setFont(dataBoldFont); + setBorder(rdLeftFooterStyle, BorderStyle.THIN, BorderStyle.DOUBLE, BorderStyle.DOUBLE, BorderStyle.THIN); + rdLeftFooterStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdLeftFooterStyle.setAlignment(HorizontalAlignment.CENTER); + + rdRightFooterStyle = workbook.createCellStyle(); + rdRightFooterStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex()); + rdRightFooterStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + rdRightFooterStyle.setFont(dataBoldFont); + setBorder(rdRightFooterStyle, BorderStyle.THIN, BorderStyle.DOUBLE, BorderStyle.THIN, BorderStyle.DOUBLE); + rdRightFooterStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdRightFooterStyle.setAlignment(HorizontalAlignment.CENTER); + + + // footer表单公式样式 + rdFooterFormulaStyle = workbook.createCellStyle(); + rdFooterFormulaStyle.cloneStyleFrom(rdFooterStyle); + rdFooterFormulaStyle.setDataFormat(workbook.createDataFormat().getFormat("0.00")); + + // 百分比样式 + rdFormulaPercentageStyle = workbook.createCellStyle(); + rdFormulaPercentageStyle.cloneStyleFrom(rdDataStyle); + rdFormulaPercentageStyle.setFillForegroundColor(IndexedColors.LIGHT_ORANGE.getIndex()); + rdFormulaPercentageStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + rdFormulaPercentageStyle.setDataFormat(workbook.createDataFormat().getFormat("0.00%")); + + rdFooterFormulaPercentageStyle = workbook.createCellStyle(); + rdFooterFormulaPercentageStyle.cloneStyleFrom(rdFooterStyle); + rdFooterFormulaPercentageStyle.setDataFormat(workbook.createDataFormat().getFormat("0.00%")); + + + rdFooterRightFormulaStyle = workbook.createCellStyle(); + rdFooterRightFormulaStyle.cloneStyleFrom(rdRightFooterStyle); + rdFooterRightFormulaStyle.setDataFormat(workbook.createDataFormat().getFormat("0.00")); + + rdInstructionStyle = workbook.createCellStyle(); + rdInstructionStyle.setFont(yaheiNormal10Font); + rdInstructionStyle.setWrapText(true); + + rdFormulaStyle = workbook.createCellStyle(); + rdFormulaStyle.cloneStyleFrom(rdDataStyle); + rdFormulaStyle.setFillForegroundColor(IndexedColors.LIGHT_ORANGE.getIndex()); + rdFormulaStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + rdFormulaStyle.setDataFormat(workbook.createDataFormat().getFormat("0.00")); + + rdRightFormulaStyle = workbook.createCellStyle(); + rdRightFormulaStyle.cloneStyleFrom(rdRightDataStyle); + rdRightFormulaStyle.setFillForegroundColor(IndexedColors.LIGHT_ORANGE.getIndex()); + rdRightFormulaStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); + rdRightFormulaStyle.setDataFormat(workbook.createDataFormat().getFormat("0.00")); + + + rdFormDataStyle = workbook.createCellStyle(); + Font rdFormDataFont = workbook.createFont(); + rdFormDataFont.setFontName("微软雅黑"); + rdFormDataFont.setFontHeightInPoints((short) 9); + rdFormDataStyle.setFont(rdFormDataFont); + rdFormDataStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdFormDataStyle.setAlignment(HorizontalAlignment.LEFT); + + rdFormStyle = workbook.createCellStyle(); + rdFormStyle.setFont(rdFormDataFont); + rdFormStyle.setVerticalAlignment(VerticalAlignment.CENTER); + rdFormStyle.setAlignment(HorizontalAlignment.LEFT); + + // 公式样式 + rdFormFormulaStyle = workbook.createCellStyle(); + rdFormFormulaStyle.cloneStyleFrom(rdFormDataStyle); + rdFormFormulaStyle.setDataFormat(workbook.createDataFormat().getFormat("0.00")); + + + } + /** * 创建表单样式 */ @@ -223,6 +607,7 @@ public abstract class BaseExcelExporter { setBorder(leftMergeStyle, BorderStyle.MEDIUM); } + /** * 设置边框 */ @@ -238,6 +623,29 @@ public abstract class BaseExcelExporter { style.setRightBorderColor(IndexedColors.BLACK.getIndex()); } + /** + * 设置边框 + */ + protected void setBorder(CellStyle style, BorderStyle topBorderStyle, BorderStyle bottomBorderStyle, + BorderStyle leftBorderStyle, BorderStyle rightBorderStyle) { + + if (topBorderStyle != null) { + style.setBorderTop(topBorderStyle); + style.setTopBorderColor(IndexedColors.BLACK.getIndex()); + } + + if (bottomBorderStyle != null) { + style.setBorderBottom(bottomBorderStyle); + style.setBottomBorderColor(IndexedColors.BLACK.getIndex()); + } + + style.setBorderLeft(leftBorderStyle); + style.setBorderRight(rightBorderStyle); + + style.setLeftBorderColor(IndexedColors.BLACK.getIndex()); + style.setRightBorderColor(IndexedColors.BLACK.getIndex()); + } + /** * 创建合并单元格 */ @@ -457,9 +865,9 @@ public abstract class BaseExcelExporter { // 1. 先横向合并前两列(A列和B列) // 在数据区域的第一行创建"人工费"单元格 Row firstDataRow = getOrCreateRow(sheet, dataStartRow); - Cell leftMergeCell = firstDataRow.createCell(0); + Cell leftMergeCell = firstDataRow.createCell(firstCol); leftMergeCell.setCellValue(cellValue); - + leftMergeStyle.setWrapText(true); // 创建左侧合并单元格的样式 leftMergeCell.setCellStyle(leftMergeStyle); @@ -544,7 +952,7 @@ public abstract class BaseExcelExporter { /** * 如果不足指定行数,则填充空行(可指定样式和公式) */ - protected int fillEmptyRow(Sheet sheet, int rowNum, int startCol, int endCol, int fillRowNum,String formula, CellStyle style) { + protected int fillEmptyRow(Sheet sheet, int rowNum, int startCol, int endCol, int fillRowNum, String formula, CellStyle style) { while (rowNum < fillRowNum) { Row dataRow = sheet.getRow(rowNum); if (dataRow == null) { @@ -676,4 +1084,225 @@ public abstract class BaseExcelExporter { style.setLeftBorderColor(borderColor); style.setRightBorderColor(borderColor); } + + + + /** + * 创建部分文本加粗的合并单元格 + * @param sheet 工作表 + * @param startRow 起始行 + * @param endRow 结束行 + * @param startCol 起始列 + * @param endCol 结束列 + * @param fullText 完整文本 + * @param boldSegments 加粗段落列表 [start, end] + * @param baseStyle 基础样式 + * @param rowHeight 行高 + */ + protected void createMergedCellWithPartialBold( + Sheet sheet, + int startRow, int endRow, + int startCol, int endCol, + String fullText, + List boldSegments, + CellStyle baseStyle, + float rowHeight) { + + // 1. 创建合并区域 + CellRangeAddress mergedRegion = new CellRangeAddress( + startRow, endRow, startCol, endCol); + sheet.addMergedRegion(mergedRegion); + + // 2. 创建主单元格 + Row mainRow = getOrCreateRow(sheet, startRow); + mainRow.setHeightInPoints(rowHeight); + Cell mainCell = mainRow.createCell(startCol); + + // 3. 创建富文本 + RichTextString richText = workbook.getCreationHelper() + .createRichTextString(fullText); + + // 4. 默认使用基础字体的正常版本 + Font normalFont = getNormalFont(baseStyle); + richText.applyFont(0, fullText.length(), normalFont); + + // 5. 应用加粗段落 + for (TextSegment segment : boldSegments) { + if (segment.isValid() && segment.isWithinBounds(fullText.length())) { + Font boldFont = getBoldFont(baseStyle, segment.getColor()); + richText.applyFont(segment.getStart(), segment.getEnd(), boldFont); + } + } + + mainCell.setCellValue(richText); + + // 6. 设置样式(保留原有的边框、对齐等) + if (baseStyle != null) { + mainCell.setCellStyle(baseStyle); + } + + // 7. 修复合并单元格的边框 + fixMergedCellBorders(sheet, mergedRegion, baseStyle); + } + + + /** + * 创建带关键词加粗的合并单元格 + */ + protected void createMergedCellWithBoldKeywords( + Sheet sheet, + int startRow, int endRow, + int startCol, int endCol, + String text, + List keywords, + CellStyle baseStyle, + float rowHeight) { + + List boldSegments = new ArrayList<>(); + + for (String keyword : keywords) { + int index = 0; + while (index < text.length()) { + int foundIndex = text.indexOf(keyword, index); + if (foundIndex >= 0) { + boldSegments.add(new TextSegment(foundIndex, foundIndex + keyword.length())); + index = foundIndex + keyword.length(); + } else { + break; + } + } + } + + // 创建合并单元格 + createMergedCellWithPartialBold(sheet, startRow, endRow, + startCol, endCol, text, boldSegments, baseStyle,rowHeight); + } + + + /** + * 获取基础字体的正常版本 + */ + private Font getNormalFont(CellStyle style) { + if (style == null) { + return yaheiNormal10Font; + } + + Font existingFont = workbook.getFontAt(style.getFontIndex()); + Font normalFont = workbook.createFont(); + + // 复制所有属性,但取消加粗 + normalFont.setFontName(existingFont.getFontName()); + normalFont.setFontHeightInPoints(existingFont.getFontHeightInPoints()); + normalFont.setColor(existingFont.getColor()); + normalFont.setItalic(existingFont.getItalic()); + normalFont.setUnderline(existingFont.getUnderline()); + + return normalFont; + } + + /** + * 获取加粗字体(可指定颜色) + */ + private Font getBoldFont(CellStyle style, Short color) { + if (style == null) { + Font boldFont = workbook.createFont(); + boldFont.setBold(true); + boldFont.setFontName("微软雅黑"); + boldFont.setFontHeightInPoints((short) 10); + if (color != null) { + boldFont.setColor(color); + } + return boldFont; + } + + Font existingFont = workbook.getFontAt(style.getFontIndex()); + Font boldFont = workbook.createFont(); + + // 复制所有属性,并设置为加粗 + boldFont.setFontName(existingFont.getFontName()); + boldFont.setFontHeightInPoints(existingFont.getFontHeightInPoints()); + boldFont.setBold(true); + + if (color != null) { + boldFont.setColor(color); + } else { + boldFont.setColor(existingFont.getColor()); + } + + boldFont.setItalic(existingFont.getItalic()); + boldFont.setUnderline(existingFont.getUnderline()); + + return boldFont; + } + + + /** + * 文本段落类,用于描述加粗部分 + */ + protected static class TextSegment { + private int start; // 起始位置(包含) + private int end; // 结束位置(不包含) + private Short color; // 字体颜色 + + public TextSegment(int start, int end) { + this.start = start; + this.end = end; + } + + public TextSegment(int start, int end, Short color) { + this.start = start; + this.end = end; + this.color = color; + } + + public boolean isValid() { + return start >= 0 && end > start; + } + + public boolean isWithinBounds(int textLength) { + return start < textLength && end <= textLength; + } + + // Getters and Setters + public int getStart() { return start; } + public void setStart(int start) { this.start = start; } + + public int getEnd() { return end; } + public void setEnd(int end) { this.end = end; } + + public Short getColor() { return color; } + public void setColor(Short color) { this.color = color; } + } + + /** + * 字体样式配置类 + */ + protected static class FontStyleConfig { + private short fontSize = 10; + private boolean bold = false; + private Short color = null; + private String fontName = "微软雅黑"; + private boolean italic = false; + private boolean underline = false; + + // Getters and Setters + public short getFontSize() { return fontSize; } + public void setFontSize(short fontSize) { this.fontSize = fontSize; } + + public boolean isBold() { return bold; } + public void setBold(boolean bold) { this.bold = bold; } + + public Short getColor() { return color; } + public void setColor(Short color) { this.color = color; } + + public String getFontName() { return fontName; } + public void setFontName(String fontName) { this.fontName = fontName; } + + public boolean isItalic() { return italic; } + public void setItalic(boolean italic) { this.italic = italic; } + + public boolean isUnderline() { return underline; } + public void setUnderline(boolean underline) { this.underline = underline; } + } + } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/ExcelStyleBuilder.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/ExcelStyleBuilder.java new file mode 100644 index 00000000..47844397 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/ExcelStyleBuilder.java @@ -0,0 +1,309 @@ +package org.dromara.oa.excel; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFFont; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +/** + * @Author xins + * @Date 2025/12/29 15:25 + * @Description:Excel样式构建器 + * * 简化样式创建和管理 + */ + +public class ExcelStyleBuilder { + + private final Workbook workbook; + private final Map styleCache = new HashMap<>(); + + // 常用字体名称 + private static final String FONT_SONG = "宋体"; + private static final String FONT_YA_HEI = "微软雅黑"; + + public ExcelStyleBuilder(Workbook workbook) { + this.workbook = workbook; + } + + /** + * 创建基础样式构建器 + */ + public StyleBuilder base() { + return new StyleBuilder(); + } + + /** + * 获取缓存的样式 + */ + public CellStyle getStyle(String key) { + return styleCache.get(key); + } + + /** + * 获取或创建样式 + */ + public CellStyle getOrCreate(String key, Consumer styleConfig) { + if (styleCache.containsKey(key)) { + return styleCache.get(key); + } + + StyleBuilder builder = new StyleBuilder(); + styleConfig.accept(builder); + CellStyle style = builder.build(); + styleCache.put(key, style); + return style; + } + + /** + * 创建字体 + */ + public Font createFont(Consumer fontConfig) { + Font font = workbook.createFont(); + fontConfig.accept(font); + return font; + } + + /** + * 样式构建器内部类 + */ + public class StyleBuilder { + private CellStyle style; + private Font font; + + public StyleBuilder() { + this.style = workbook.createCellStyle(); + } + + /** + * 设置字体 + */ + public StyleBuilder font(String fontName, short fontSize, boolean bold) { + this.font = createFont(f -> { + f.setFontName(fontName); + f.setFontHeightInPoints(fontSize); + f.setBold(bold); + }); + style.setFont(font); + return this; + } + + /** + * 设置宋体字体 + */ + public StyleBuilder song(short fontSize, boolean bold) { + return font(FONT_SONG, fontSize, bold); + } + + /** + * 设置微软雅黑字体 + */ + public StyleBuilder yaHei(short fontSize, boolean bold) { + return font(FONT_YA_HEI, fontSize, bold); + } + + /** + * 设置颜色 + */ + public StyleBuilder fontColor(IndexedColors color) { + if (font == null) { + yaHei((short) 10, false); + } + font.setColor(color.getIndex()); + return this; + } + + /** + * 设置背景色 + */ + public StyleBuilder bgColor(IndexedColors color) { + style.setFillForegroundColor(color.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + return this; + } + + /** + * 设置对齐方式 + */ + public StyleBuilder align(HorizontalAlignment horizontal, VerticalAlignment vertical) { + style.setAlignment(horizontal); + style.setVerticalAlignment(vertical); + return this; + } + + /** + * 设置居中 + */ + public StyleBuilder center() { + return align(HorizontalAlignment.CENTER, VerticalAlignment.CENTER); + } + + /** + * 设置左对齐 + */ + public StyleBuilder left() { + return align(HorizontalAlignment.LEFT, VerticalAlignment.CENTER); + } + + /** + * 设置右对齐 + */ + public StyleBuilder right() { + return align(HorizontalAlignment.RIGHT, VerticalAlignment.CENTER); + } + + /** + * 设置边框 + */ + public StyleBuilder border(BorderStyle borderStyle) { + style.setBorderTop(borderStyle); + style.setBorderBottom(borderStyle); + style.setBorderLeft(borderStyle); + style.setBorderRight(borderStyle); + + short color = IndexedColors.BLACK.getIndex(); + style.setTopBorderColor(color); + style.setBottomBorderColor(color); + style.setLeftBorderColor(color); + style.setRightBorderColor(color); + return this; + } + + /** + * 设置自定义边框 + */ + public StyleBuilder border(BorderStyle top, BorderStyle bottom, + BorderStyle left, BorderStyle right) { + style.setBorderTop(top); + style.setBorderBottom(bottom); + style.setBorderLeft(left); + style.setBorderRight(right); + + short color = IndexedColors.BLACK.getIndex(); + style.setTopBorderColor(color); + style.setBottomBorderColor(color); + style.setLeftBorderColor(color); + style.setRightBorderColor(color); + return this; + } + + /** + * 设置底部边框 + */ + public StyleBuilder bottomBorder(BorderStyle borderStyle) { + style.setBorderBottom(borderStyle); + style.setBottomBorderColor(IndexedColors.BLACK.getIndex()); + return this; + } + + /** + * 设置数据格式 + */ + public StyleBuilder dataFormat(String format) { + DataFormat dataFormat = workbook.createDataFormat(); + style.setDataFormat(dataFormat.getFormat(format)); + return this; + } + + /** + * 设置数字格式 + */ + public StyleBuilder numberFormat() { + return dataFormat("0.00"); + } + + /** + * 设置百分比格式 + */ + public StyleBuilder percentFormat() { + return dataFormat("0.00%"); + } + + /** + * 设置货币格式 + */ + public StyleBuilder moneyFormat() { + return dataFormat("#,##0.00"); + } + + /** + * 设置自动换行 + */ + public StyleBuilder wrapText(boolean wrap) { + style.setWrapText(wrap); + return this; + } + + /** + * 克隆样式并自定义 + */ + public StyleBuilder clone(CellStyle source, Consumer customizer) { + this.style.cloneStyleFrom(source); + customizer.accept(this); + return this; + } + + /** + * 构建样式 + */ + public CellStyle build() { + return style; + } + + /** + * 构建并缓存 + */ + public CellStyle buildAndCache(String key) { + CellStyle builtStyle = build(); + styleCache.put(key, builtStyle); + return builtStyle; + } + } + + /** + * 常用样式键常量 + */ + public static class Keys { + // 标题样式 + public static final String TITLE = "title"; + public static final String SUBTITLE = "subtitle"; + + // 表单样式 + public static final String FORM_LEFT = "form_left"; + public static final String FORM_RIGHT = "form_right"; + public static final String FORM_FORMULA = "form_formula"; + public static final String FORM_PERCENT = "form_percent"; + + // 表头样式 + public static final String HEADER = "header"; + public static final String MERGED_HEADER = "merged_header"; + + // 数据样式 + public static final String DATA = "data"; + public static final String DATA_LEFT = "data_left"; + public static final String DATA_RIGHT = "data_right"; + public static final String DATA_BOTTOM = "data_bottom"; + public static final String DATA_CENTER = "data_center"; + + // 特殊格式 + public static final String PERCENT = "percent"; + public static final String FORMULA = "formula"; + public static final String MONEY = "money"; + public static final String FOOTER_FORMULA = "footer_formula"; + + // 备注样式 + public static final String REMARK = "remark"; + + // 合并单元格样式 + public static final String LEFT_MERGE = "left_merge"; + + // 研发预算相关 + public static final String RD_COVER_TITLE = "rd_cover_title"; + public static final String RD_COVER_SUBTITLE = "rd_cover_subtitle"; + public static final String RD_HEADER = "rd_header"; + public static final String RD_DATA = "rd_data"; + public static final String RD_DATA_BOLD = "rd_data_bold"; + public static final String RD_DATA_RED = "rd_data_red"; + } +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/MarketProjectBudgetExcelExporter.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/MarketProjectBudgetExcelExporter.java index 40599bfd..0622bd80 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/MarketProjectBudgetExcelExporter.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/MarketProjectBudgetExcelExporter.java @@ -676,7 +676,7 @@ public class MarketProjectBudgetExcelExporter extends BaseExcelExporter { Double reducePeopleNumber = detail.getReducePeopleNumber() != null ? detail.getReducePeopleNumber().doubleValue() : null; Double reduceCumulativeTime = detail.getReduceCumulativeTime() != null ? detail.getReduceCumulativeTime().doubleValue() : null; Double reduceArtificialStandard = detail.getReduceArtificialStandard() != null ? detail.getReduceArtificialStandard().doubleValue() : null; - createCell(dataRow, 8, detail.getPersonnelCategory(), dataStyle); + createCell(dataRow, 8, detail.getReducePersonnelCategory(), dataStyle); createNumericCell(dataRow, 9, reducePeopleNumber, dataStyle); createNumericCell(dataRow, 10, reduceCumulativeTime, dataStyle); createNumericCell(dataRow, 11, detail.getReduceMonthRate() == null ? BigDecimal.ZERO : @@ -860,7 +860,7 @@ public class MarketProjectBudgetExcelExporter extends BaseExcelExporter { Double reducePeopleNumber = detail.getReducePeopleNumber() != null ? detail.getReducePeopleNumber().doubleValue() : null; Double reduceCumulativeTime = detail.getReduceCumulativeTime() != null ? detail.getReduceCumulativeTime().doubleValue() : null; Double reduceArtificialStandard = detail.getReduceArtificialStandard() != null ? detail.getReduceArtificialStandard().doubleValue() : null; - createCell(dataRow, 8, detail.getPersonnelCategory(), dataStyle); + createCell(dataRow, 8, detail.getReducePersonnelCategory(), dataStyle); createNumericCell(dataRow, 9, reducePeopleNumber, dataStyle); createNumericCell(dataRow, 10, reduceCumulativeTime, dataStyle); createNumericCell(dataRow, 11, detail.getReduceMonthRate() == null ? BigDecimal.ZERO : diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/RdBudgetItemEnums.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/RdBudgetItemEnums.java new file mode 100644 index 00000000..c9020188 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/RdBudgetItemEnums.java @@ -0,0 +1,55 @@ +package org.dromara.oa.excel; + +/** + * @Author xins + * @Date 2025/12/30 17:21 + * @Description: 研发预算汇总表枚举 + */ +public enum RdBudgetItemEnums { + MATERIAL("材料费", "表2-材料费", "表2-材料费", "H"), + LABOR("人工费", "表3-人工费", "表3-人工费", "H"), + TRAVEL("差旅费", "表4-差旅费", "表4-差旅费", "M"), + TESTING("测试化验加工费", "表5-测试化验加工费","表5-测试化验加工费", "H"), + CONSULTATION("专家咨询费用", "表6-咨询费、设计费","专家费", "I"), + DESIGN("新产品设计费", "表6-咨询费、设计费", "新产品设计费", "H"), + OTHER("其他费用", "表7-其他费用", "表7-其他费用","D"); + + private final String name; + private final String sheetName; + private final String childSheetName; + private final String sheetPosition; + + RdBudgetItemEnums(String name, String sheetName, String childSheetName, String sheetPosition) { + this.name = name; + this.sheetName = sheetName; + this.childSheetName = childSheetName; + this.sheetPosition = sheetPosition; + } + + // 获取所有项目名称 + public static String[] getAllItemNames() { + RdBudgetItemEnums[] items = values(); + String[] names = new String[items.length]; + for (int i = 0; i < items.length; i++) { + names[i] = items[i].getName(); + } + return names; + } + + // getter 方法 + public String getName() { + return name; + } + + public String getSheetName() { + return sheetName; + } + + public String getChildSheetName() { + return childSheetName; + } + + public String getSheetPosition() { + return sheetPosition; + } +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/RdProjectBudgetExcelExporter.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/RdProjectBudgetExcelExporter.java index 03e323c6..75f2f19e 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/RdProjectBudgetExcelExporter.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/excel/RdProjectBudgetExcelExporter.java @@ -1,20 +1,17 @@ package org.dromara.oa.excel; import org.apache.poi.ss.usermodel.*; +import org.dromara.common.core.utils.StringUtils; import org.dromara.oa.erp.domain.*; +import org.dromara.oa.erp.domain.dto.BudgetDefinitionDTO; import org.dromara.oa.erp.domain.vo.ErpBudgetInfoVo; -import org.dromara.oa.erp.enums.LaborTypeEnum; -import org.dromara.oa.erp.enums.LiteratureTypeEnum; -import org.dromara.oa.erp.enums.RdMaterialTypeEnum; -import org.dromara.oa.erp.enums.TechTypeEnum; +import org.dromara.oa.erp.enums.*; import org.springframework.stereotype.Component; import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; +import java.util.*; /** * @Author xins @@ -24,21 +21,10 @@ import java.util.Optional; @Component public class RdProjectBudgetExcelExporter extends BaseExcelExporter { - private static final String SHEET_BUDGET = "预算表"; - private static final String SHEET_EQUIPMENT = "设备费"; - private static final String SHEET_MATERIAL = "材料费"; - private static final String SHEET_TRAVEL_MEETING = "会议差旅交流"; - private static final String SHEET_LABOR_SERVICE = "人工劳务咨询"; - private static final String SHEET_DOCUMENT = "资料文献费"; - private static final String SHEET_TESTING = "测试化验费"; - private static final String SHEET_OTHER = "其他"; - - private static final String BUDGET_TRAVEL = "差旅费"; - private static final String BUDGET_MEETING = "会议费"; - private static final String BUDGET_INTERNATIONAL = "国际合作与交流费"; - private static final String BUDGET_TECH = "咨询开发费"; - private static final String BUDGET_LABOR = "人工费"; - private static final String BUDGET_SERVICE = "劳务费"; + //研发 + private static final String RD_SHEET_COVER = "封面"; + private static final String RD_SHEET_PREPARATION = "预算编制标准说明"; + private static final String RD_SHEET_BUDGET = "表1-预算汇总"; @Override public byte[] exportToByteArray(ErpBudgetInfoVo budget) throws IOException { @@ -48,8 +34,15 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { createStyles(); clearTotalRowMap(); + //创建封面 + createRdCoverSheet(budget); + + //创建预算编制标准说明 + createRdPreparationSheet(budget); + // 先创建预算表的空表在第一个 - Sheet sheet = workbook.createSheet(SHEET_BUDGET); + Sheet sheet = workbook.createSheet(RD_SHEET_BUDGET); + // 创建明细表并记录总计行位置 createDetailSheets(budget); @@ -57,6 +50,12 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { // 创建预算总表(引用明细表的合计行) createBudgetSheet(sheet, budget); + // 设置活动工作表(索引从0开始) + workbook.setActiveSheet(2); // 设置第三个工作表为活动工作表 + + // 设置选中状态(选中的工作表会有边框) + workbook.setSelectedTab(2); // 选中第三个工作表 + // 写入字节数组 return writeToByteArray(); @@ -73,8 +72,15 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { createStyles(); clearTotalRowMap(); + //创建封面 + createRdCoverSheet(budget); + + //创建预算编制标准说明 + createRdPreparationSheet(budget); + // 先创建预算表的空表在第一个 - Sheet sheet = workbook.createSheet(SHEET_BUDGET); + Sheet sheet = workbook.createSheet(RD_SHEET_BUDGET); + // 创建明细表并记录总计行位置 createDetailSheets(budget); @@ -82,6 +88,12 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { // 创建预算总表(引用明细表的合计行) createBudgetSheet(sheet, budget); + // 设置活动工作表(索引从0开始) + workbook.setActiveSheet(2); // 设置第三个工作表为活动工作表 + + // 设置选中状态(选中的工作表会有边框) + workbook.setSelectedTab(2); // 选中第三个工作表 + // 写入文件 writeToFile(filePath); @@ -94,496 +106,145 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { * 创建所有明细表 */ private void createDetailSheets(ErpBudgetInfoVo budget) { - // 创建设备费明细表 - Integer equipmentTotalRow = createEquipmentSheet(budget); - addTotalRowMapping(SHEET_EQUIPMENT, equipmentTotalRow); - // 创建材料费明细表 Integer materialTotalRow = createMaterialSheet(budget); - addTotalRowMapping(SHEET_MATERIAL, materialTotalRow); + addTotalRowMapping(RdBudgetItemEnums.MATERIAL.getChildSheetName(), materialTotalRow); - // 创建差旅费、会议费、国际交流费明细表 - Integer travelMeetingTotalRow = createTravelMeetingSheet(budget); - addTotalRowMapping(SHEET_TRAVEL_MEETING, travelMeetingTotalRow); - - // 创建人工费、劳务费、咨询开发费明细表 + // 创建人工费明细表 Integer laborServiceTotalRow = createLaborServiceSheet(budget); - addTotalRowMapping(SHEET_LABOR_SERVICE, laborServiceTotalRow); + addTotalRowMapping(RdBudgetItemEnums.LABOR.getChildSheetName(), laborServiceTotalRow); - // 创建资料/文献费明细表 - Integer documentTotalRow = createDocumentSheet(budget); - addTotalRowMapping(SHEET_DOCUMENT, documentTotalRow); + // 创建差旅费明细表 + Integer travelTotalRow = createTravelSheet(budget); - // 创建测试化验费明细表 + // 创建测试化验加工费明细表 Integer testingTotalRow = createTestingSheet(budget); - addTotalRowMapping(SHEET_TESTING, testingTotalRow); + addTotalRowMapping(RdBudgetItemEnums.TESTING.getChildSheetName(), testingTotalRow); + + // 创建测试化验加工费明细表 + Integer techTotalRow = createTechSheet(budget); // 创建其他费用明细表 Integer otherTotalRow = createOtherSheet(budget); - addTotalRowMapping(SHEET_OTHER, otherTotalRow); + addTotalRowMapping(RdBudgetItemEnums.OTHER.getChildSheetName(), otherTotalRow); } - // =========== 预算总表相关方法 =========== /** - * 创建预算总表 + * 创建研发预算封面 + * + * @param budget */ - private void createBudgetSheet(Sheet sheet, ErpBudgetInfoVo budget) { - int rowNum = 0; + private void createRdCoverSheet(ErpBudgetInfoVo budget) { + Sheet sheet = workbook.createSheet(RD_SHEET_COVER); - // 创建表头 - rowNum = createBudgetSheetHeader(sheet, budget, rowNum); + // 表头 + Row titleRow = sheet.createRow(0); + createMergedCell(sheet, titleRow, 0, 12, "青岛海威物联科技有限公司", rdCoverTitleStyle); - // 创建预算科目数据 - rowNum = createBudgetSubjects(sheet, rowNum); + Row subtitleRow = sheet.createRow(2); + createMergedCell(sheet, subtitleRow, 0, 12, "研 发 项 目 预 算", rdCoverSubtitleStyle); - // 创建合计行 - rowNum = createBudgetTotalRow(sheet, rowNum); + // form + Row projectNameFormRow = sheet.createRow(6); + createMergedCell(sheet, projectNameFormRow, 2, 4, "项目名称:", rdCoverFormKeyStyle); + createMergedCell(sheet, projectNameFormRow, 5, 9, budget.getProjectName(), rdCoverFormValueStyle); + Row projectCodeFormRow = sheet.createRow(7); + createMergedCell(sheet, projectCodeFormRow, 2, 4, "项目编号:", rdCoverFormKeyStyle); + createMergedCell(sheet, projectCodeFormRow, 5, 9, budget.getProjectCode(), rdCoverFormValueStyle); + Row projectManagerFormRow = sheet.createRow(8); + createMergedCell(sheet, projectManagerFormRow, 2, 4, "项目经理:", rdCoverFormKeyStyle); + createMergedCell(sheet, projectManagerFormRow, 5, 9, budget.getManagerName(), rdCoverFormValueStyle); - // 创建编制审核信息 - rowNum = createBudgetSignInfo(sheet, rowNum, budget); + Row dateFormRow = sheet.createRow(11); + Date preparationDate = budget.getPreparationDate(); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(preparationDate); + createMergedCell(sheet, dateFormRow, 2, 4, "编制日期", rdCoverFormKeyStyle); + createCell(dateFormRow, 5, String.valueOf(calendar.get(Calendar.YEAR)), rdCoverFormValueStyle); + createCell(dateFormRow, 6, "年", rdCoverFormKeyStyle); + createCell(dateFormRow, 7, String.valueOf(calendar.get(Calendar.MONTH)+1), rdCoverFormValueStyle); + createCell(dateFormRow, 8, "月", rdCoverFormKeyStyle); + createCell(dateFormRow, 9, String.valueOf(calendar.get(Calendar.DAY_OF_MONTH)), rdCoverFormValueStyle); + createCell(dateFormRow, 10, "日", rdCoverFormKeyStyle); - // 设置列宽 - setBudgetSheetColumnWidths(sheet); + Row chargeFormRow = sheet.createRow(13); + String cellValue = String.format("部门负责人:%s 财务负责人:%s 项目经理:%s", + StringUtils.isNotEmpty(budget.getProductManagerName()) ? budget.getProductManagerName() : " ", + StringUtils.isNotEmpty(budget.getApproveUserName()) ? budget.getApproveUserName() : " ", + StringUtils.isNotEmpty(budget.getManagerName()) ? budget.getManagerName() : ""); + createMergedCell(sheet, chargeFormRow, 0, 12, cellValue , rdCoverFormKeyStyle); } + /** - * 创建预算表头 + * 创建研发预算编制标准说明 + * + * @param budget */ - private int createBudgetSheetHeader(Sheet sheet, ErpBudgetInfoVo budget, int startRowNum) { + private void createRdPreparationSheet(ErpBudgetInfoVo budget) { + Sheet sheet = workbook.createSheet(RD_SHEET_PREPARATION); + + int rowNum = 1; + + rowNum = createRdPreparationSheetHeader(sheet, rowNum); + + createRdPreparationDataRows(sheet, rowNum, budget); + + setColumnWidths(sheet, new int[]{5, 15, 20, 50}); + } + + + /** + * 创建研发预算编制标准说明表头 + * + * @param sheet + * @param startRowNum + * @return + */ + private int createRdPreparationSheetHeader(Sheet sheet, int startRowNum) { int rowNum = startRowNum; // 表头 - Row titleRow1 = sheet.createRow(rowNum++); - createMergedCell(sheet, titleRow1, 0, 2, "表一", titleStyle); - Row titleRow2 = sheet.createRow(rowNum++); - createMergedCell(sheet, titleRow2, 0, 2, "项目经费预算表", titleStyle); - - // 项目信息 - Row projectRow = sheet.createRow(rowNum++); - createMergedCell(sheet, projectRow, 0, 1, "项目名称:" + (budget.getProjectName() == null ? "" : budget.getProjectName()), formLeftStyle); - createCell(projectRow, 2, "项目号:" + (budget.getProjectCode() == null ? "" : budget.getProjectCode()), formLeftStyle); - - // 预算期间 - Row periodRow = sheet.createRow(rowNum++); - createCell(periodRow, 0, "项目预算期间:", formLeftStyle); - String period = budget.getDuringOperation() == null ? "" : budget.getDuringOperation(); - createCell(periodRow, 1, period, formLeftStyle); - createCell(periodRow, 2, "金额单位:万元", formLeftStyle); + Row headerRow = sheet.createRow(rowNum++); + createCell(headerRow, 1, "序号", rdLeftHeaderStyle); + createCell(headerRow, 2, "预算科目名称", rdHeaderStyle); + createCell(headerRow, 3, "预算科目定义", rdRightHeaderStyle); return rowNum; } /** - * 创建预算科目 + * 创建研发预算编制标准说明数据 + * + * @param sheet + * @param startRowNum */ - private int createBudgetSubjects(Sheet sheet, int startRowNum) { + private void createRdPreparationDataRows(Sheet sheet, int startRowNum, ErpBudgetInfoVo budget) { int rowNum = startRowNum; - - // 预算科目表头 - Row headerRow = sheet.createRow(rowNum++); - createCell(headerRow, 0, "序号", headerStyle); - createCell(headerRow, 1, "预算科目名称", headerStyle); - createCell(headerRow, 2, "项目经费", headerStyle); - - // 预算科目数据 - String[] budgetItems = { - "设备费", "材料费", "差旅费", "会议费", "国际合作与交流费", - "咨询开发费", "人工费", "劳务费", "资料/文献费", "测试化验费", "其他费用" - }; - - for (int i = 0; i < budgetItems.length; i++) { + List budgetDefinitionDTOList = budget.getBudgetDefinitionDTOList() == null + ? new ArrayList<>() : budget.getBudgetDefinitionDTOList(); + int size = budgetDefinitionDTOList.size(); + for (int i = 0; i < size; i++) { + BudgetDefinitionDTO budgetDefinitionDTO = budgetDefinitionDTOList.get(i); Row dataRow = sheet.createRow(rowNum++); - createCell(dataRow, 0, String.valueOf(i + 1), dataStyle); - createCell(dataRow, 1, budgetItems[i], dataStyle); - - setupBudgetSubjectFormula(dataRow, budgetItems[i]); - } - - return rowNum; - } - - /** - * 设置预算科目公式 - */ - private void setupBudgetSubjectFormula(Row row, String subject) { - switch (subject) { - case "设备费": - setupEquipmentFormula(row); - break; - case "材料费": - setupMaterialFormula(row); - break; - case "差旅费": - setupTravelFormula(row); - break; - case "会议费": - setupMeetingFormula(row); - break; - case "国际合作与交流费": - setupInternationalFormula(row); - break; - case "咨询开发费": - setupTechFormula(row); - break; - case "人工费": - setupLaborFormula(row); - break; - case "劳务费": - setupServiceFormula(row); - break; - case "资料/文献费": - setupDocumentFormula(row); - break; - case "测试化验费": - setupTestingFormula(row); - break; - case "其他费用": - setupOtherFormula(row); - break; - default: - createCell(row, 2, "", dataStyle); - } - } - - /** - * 创建预算合计行 - */ - private int createBudgetTotalRow(Sheet sheet, int startRowNum) { - int rowNum = startRowNum; - - Row totalRow = sheet.createRow(rowNum++); - createMergedCell(sheet, totalRow, 0, 1, "合计", headerStyle); - - Cell totalCell = totalRow.createCell(2); - totalCell.setCellFormula("SUM(C6:C16)"); - totalCell.setCellStyle(formulaStyle); - - return rowNum; - } - - /** - * 创建预算编制审核信息 - */ - private int createBudgetSignInfo(Sheet sheet, int startRowNum, ErpBudgetInfoVo budget) { - int rowNum = startRowNum; - - rowNum += 1; - Row signRow1 = sheet.createRow(rowNum++); - createCell(signRow1, 0, "编制(项目经理):", formLeftStyle); - createCell(signRow1, 1, budget.getManagerName() == null ? "" : budget.getManagerName(), formLeftStyle); - - rowNum += 4; - Row signRow2 = sheet.createRow(rowNum); - createCell(signRow2, 0, "审核(评审组长):", formLeftStyle); - createCell(signRow2, 1, budget.getApproveUserName() == null ? "" : budget.getApproveUserName(), formLeftStyle); - - return rowNum; - } - - /** - * 设置预算表列宽 - */ - private void setBudgetSheetColumnWidths(Sheet sheet) { - setColumnWidths(sheet, new int[]{20, 40, 30}); - } - - // =========== 公式设置方法 =========== - - /** - * 设置设备费公式 - */ - private void setupEquipmentFormula(Row row) { - Integer totalRow = getTotalRow(SHEET_EQUIPMENT); - if (totalRow != null) { - Cell cell = row.createCell(2); - cell.setCellFormula(String.format("%s!F%d", SHEET_EQUIPMENT, totalRow + 1)); - cell.setCellStyle(formulaStyle); - } else { - createCell(row, 2, "", dataStyle); - } - } - - /** - * 设置材料费公式 - */ - private void setupMaterialFormula(Row row) { - Integer totalRow = getTotalRow(SHEET_MATERIAL); - if (totalRow != null) { - Cell cell = row.createCell(2); - cell.setCellFormula(String.format("%s!F%d", SHEET_MATERIAL, totalRow + 1)); - cell.setCellStyle(formulaStyle); - } else { - createCell(row, 2, "", dataStyle); - } - } - - /** - * 设置差旅费公式 - */ - private void setupTravelFormula(Row row) { - Integer travelTotalRow = getTotalRow(BUDGET_TRAVEL); - if (travelTotalRow != null) { - Cell cell = row.createCell(2); - cell.setCellFormula(String.format("%s!L%d", SHEET_TRAVEL_MEETING, travelTotalRow)); - cell.setCellStyle(formulaStyle); - } else { - createCell(row, 2, "", dataStyle); - } - } - - /** - * 设置会议费公式 - */ - private void setupMeetingFormula(Row row) { - Integer meetingTotalRow = getTotalRow(BUDGET_MEETING); - if (meetingTotalRow != null) { - Cell cell = row.createCell(2); - cell.setCellFormula(String.format("%s!L%d", SHEET_TRAVEL_MEETING, meetingTotalRow)); - cell.setCellStyle(formulaStyle); - } else { - createCell(row, 2, "", dataStyle); - } - } - - /** - * 设置国际交流费公式 - */ - private void setupInternationalFormula(Row row) { - Integer internationalTotalRow = getTotalRow(BUDGET_INTERNATIONAL); - if (internationalTotalRow != null) { - Cell cell = row.createCell(2); - cell.setCellFormula(String.format("%s!L%d", SHEET_TRAVEL_MEETING, internationalTotalRow)); - cell.setCellStyle(formulaStyle); - } else { - createCell(row, 2, "", dataStyle); - } - } - - /** - * 设置技术咨询费公式 - */ - private void setupTechFormula(Row row) { - Integer techTotalRow = getTotalRow(BUDGET_TECH); - if (techTotalRow != null) { - Cell cell = row.createCell(2); - cell.setCellFormula(String.format("%s!I%d", SHEET_LABOR_SERVICE, techTotalRow)); - cell.setCellStyle(formulaStyle); - } else { - createCell(row, 2, "", dataStyle); - } - } - - /** - * 设置人工费公式 - */ - private void setupLaborFormula(Row row) { - Integer laborTotalRow = getTotalRow(BUDGET_LABOR); - if (laborTotalRow != null) { - Cell cell = row.createCell(2); - cell.setCellFormula(String.format("%s!I%d", SHEET_LABOR_SERVICE, laborTotalRow)); - cell.setCellStyle(formulaStyle); - } else { - createCell(row, 2, "", dataStyle); - } - } - - /** - * 设置劳务费公式 - */ - private void setupServiceFormula(Row row) { - Integer serviceTotalRow = getTotalRow(BUDGET_SERVICE); - if (serviceTotalRow != null) { - Cell cell = row.createCell(2); - cell.setCellFormula(String.format("%s!I%d", SHEET_LABOR_SERVICE, serviceTotalRow)); - cell.setCellStyle(formulaStyle); - } else { - createCell(row, 2, "", dataStyle); - } - } - - /** - * 设置资料文献费公式 - */ - private void setupDocumentFormula(Row row) { - Integer totalRow = getTotalRow(SHEET_DOCUMENT); - if (totalRow != null) { - Cell cell = row.createCell(2); - cell.setCellFormula(String.format("%s!B%d", SHEET_DOCUMENT, totalRow + 1)); - cell.setCellStyle(formulaStyle); - } else { - createCell(row, 2, "", dataStyle); - } - } - - /** - * 设置测试化验费公式 - */ - private void setupTestingFormula(Row row) { - Integer totalRow = getTotalRow(SHEET_TESTING); - if (totalRow != null) { - Cell cell = row.createCell(2); - cell.setCellFormula(String.format("%s!G%d", SHEET_TESTING, totalRow + 1)); - cell.setCellStyle(formulaStyle); - } else { - createCell(row, 2, "", dataStyle); - } - } - - /** - * 设置其他费用公式 - */ - private void setupOtherFormula(Row row) { - Integer totalRow = getTotalRow(SHEET_OTHER); - if (totalRow != null) { - Cell cell = row.createCell(2); - cell.setCellFormula(String.format("%s!C%d", SHEET_OTHER, totalRow + 1)); - cell.setCellStyle(formulaStyle); - } else { - createCell(row, 2, "", dataStyle); - } - } - - // =========== 设备费明细表相关方法 =========== - - /** - * 创建设备费明细表 - */ - private Integer createEquipmentSheet(ErpBudgetInfoVo budget) { - Sheet sheet = workbook.createSheet(SHEET_EQUIPMENT); - int rowNum = 0; - - // 创建表头 - rowNum = createEquipmentSheetHeader(sheet, rowNum); - - // 创建数据行 - int dataStartRow = rowNum + 1; - rowNum = createEquipmentDataRows(sheet, budget, rowNum); - - // 填充空行 - rowNum = fillEquipmentEmptyRow(sheet, rowNum, 0, 5, 11, dataStyle); - - // 创建合计行 - rowNum = createEquipmentTotalRow(sheet, dataStartRow, rowNum); - - // 设置列宽 - setEquipmentSheetColumnWidths(sheet); - - return rowNum; - } - - private int fillEquipmentEmptyRow(Sheet sheet, int rowNum, int startCol, int endCol, int fillRowNum, CellStyle style) { - while (rowNum < fillRowNum) { - Row dataRow = sheet.getRow(rowNum); - if (dataRow == null) { - dataRow = sheet.createRow(rowNum); + if (i == size - 1) { + createCell(dataRow, 1, budgetDefinitionDTO.getSerialNumber().toString(), rdLeftBottomDataStyle); + createCell(dataRow, 2, budgetDefinitionDTO.getSubjectName(), + budgetDefinitionDTO.getSubjectFlag().equals("2") ? rdBottomDataRedBoldStyle : rdDataBoldStyle); + createCell(dataRow, 3, budgetDefinitionDTO.getSubjectDefinition(), + rdRightBottomDataStyle); + } else { + createCell(dataRow, 1, budgetDefinitionDTO.getSerialNumber().toString(), rdLeftDataStyle); + createCell(dataRow, 2, budgetDefinitionDTO.getSubjectName(), + budgetDefinitionDTO.getSubjectFlag().equals("2") ? rdDataRedBoldStyle : rdDataBoldStyle); + createCell(dataRow, 3, budgetDefinitionDTO.getSubjectDefinition(), + rdRightDataStyle); } - - for (int i = startCol; i <= endCol - 1; i++) { - Double value = null; - createNumericCell(dataRow, i, value, style); - } - - Cell amountCell = dataRow.createCell(endCol); - amountCell.setCellFormula(String.format("D%d*E%d/10000", rowNum + 1, rowNum + 1)); - amountCell.setCellStyle(formulaStyle); - - rowNum++; } - return rowNum; } - /** - * 创建设备费表头 - */ - private int createEquipmentSheetHeader(Sheet sheet, int startRowNum) { - int rowNum = startRowNum; - - // 表头 - Row titleRow1 = sheet.createRow(rowNum++); - createMergedCell(sheet, titleRow1, 0, 5, "表二", titleStyle); - - Row titleRow2 = sheet.createRow(rowNum++); - createMergedCell(sheet, titleRow2, 0, 5, "购置设备预算明细表", titleStyle); - - // 表头 - Row headerRow = sheet.createRow(rowNum++); - createCell(headerRow, 2, "设备", headerStyle); - createCell(headerRow, 3, "单价", headerStyle); - createCell(headerRow, 4, "数量", headerStyle); - createCell(headerRow, 5, "金额", headerStyle); - - Row headerRow1 = sheet.createRow(rowNum++); - createCell(headerRow1, 2, "型号", headerStyle); - createCell(headerRow1, 3, "(元/台件)", headerStyle); - createCell(headerRow1, 4, "(台件)", headerStyle); - createCell(headerRow1, 5, "(万元)", headerStyle); - - createVerticalMergedCell(sheet, 2, 3, 0, "序号", headerStyle); - createVerticalMergedCell(sheet, 2, 3, 1, "设备名称", headerStyle); - - return rowNum; - } - - /** - * 创建设备费数据行 - */ - private int createEquipmentDataRows(Sheet sheet, ErpBudgetInfoVo budget, int startRowNum) { - int rowNum = startRowNum; - - - - return rowNum; - } - - /** - * 创建单个设备费数据行 - */ - private int createEquipmentDataRow(Sheet sheet, ErpRdBudgetEquipmentCost detail, int rowNum) { - Row dataRow = sheet.createRow(rowNum++); - - createCell(dataRow, 0, detail.getSortOrder().toString(), dataStyle); - createCell(dataRow, 1, detail.getEquipmentName(), dataStyle); - createCell(dataRow, 2, detail.getEquipmentSpec(), dataStyle); - Double unitPrice = detail.getUnitPrice() != null ? detail.getUnitPrice().doubleValue() : null; - createNumericCell(dataRow, 3, unitPrice, dataStyle); - Double amount = detail.getAmount() != null ? detail.getAmount().doubleValue() : null; - createNumericCell(dataRow, 4, amount, dataStyle); - - // 设置金额公式 - Cell amountCell = dataRow.createCell(5); - amountCell.setCellFormula(String.format("D%d*E%d/10000", rowNum, rowNum)); - amountCell.setCellStyle(formulaStyle); - - - return rowNum; - } - - /** - * 创建设备费合计行 - */ - private int createEquipmentTotalRow(Sheet sheet, int dataStartRow, int rowNum) { - Row totalRow = sheet.createRow(rowNum); - - createMergedCell(sheet, totalRow, 0, 1, "合计", headerStyle); - createCell(totalRow, 2, "/", headerStyle); - createCell(totalRow, 3, "/", headerStyle); - createCell(totalRow, 4, "", headerStyle); - - Cell totalCell = totalRow.createCell(5); - totalCell.setCellFormula(String.format("SUM(F%d:F%d)", dataStartRow, rowNum)); - totalCell.setCellStyle(formulaStyle); - - return rowNum; - } - - /** - * 设置设备费表列宽 - */ - private void setEquipmentSheetColumnWidths(Sheet sheet) { - setColumnWidths(sheet, new int[]{15, 30, 20, 20, 20, 20}); - } // =========== 材料费明细表相关方法 =========== @@ -591,12 +252,10 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { * 创建材料费明细表 */ private Integer createMaterialSheet(ErpBudgetInfoVo budget) { - Sheet sheet = workbook.createSheet(SHEET_MATERIAL); + Sheet sheet = workbook.createSheet(RdBudgetItemEnums.MATERIAL.getSheetName()); int rowNum = 0; List rdBudgetMaterialCostList = prepareMaterialCostData(budget); - List mainMaterialCostList = filterMaterialCostByType(rdBudgetMaterialCostList, RdMaterialTypeEnum.MAIN_MATERIAL.getCode()); - List otherMaterialCostList = filterMaterialCostByType(rdBudgetMaterialCostList, RdMaterialTypeEnum.OTHER_MATERIAL.getCode()); // 创建表头 @@ -604,56 +263,63 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { // 创建数据行 int dataStartRow = rowNum + 1; - rowNum = createMaterialDataRows(sheet, mainMaterialCostList, rowNum); + rowNum = createMaterialDataRows(sheet, rdBudgetMaterialCostList, rowNum); // 填充空行 - rowNum = fillMaterialEmptyRow(sheet, rowNum, 0, 5, 11, dataStyle); + rowNum = fillMaterialEmptyRow(sheet, rdBudgetMaterialCostList.size(), rowNum, 10); // 创建小计行 - rowNum = createMaterialSubtotalRows(sheet, dataStartRow, rowNum, otherMaterialCostList); + rowNum = createMaterialTotalRows(sheet, dataStartRow, rowNum); // 设置列宽 setMaterialSheetColumnWidths(sheet); + //设置填写说明 + createMaterialInstruction(sheet, rowNum); return rowNum; } /** - * 准备技术服务费数据 + * 准备材料费数据 */ private List prepareMaterialCostData(ErpBudgetInfoVo budget) { - List budgetMaterialCostList = budget.getErpRdBudgetMaterialCostList() == null ? + return budget.getErpRdBudgetMaterialCostList() == null ? new ArrayList<>() : budget.getErpRdBudgetMaterialCostList(); - return budgetMaterialCostList; - } - - /** - * 按类型筛选技术服务费数据 - */ - private List filterMaterialCostByType(List list, String type) { - return list.stream() - .filter(item -> item.getMaterialType().equals(type)) - .toList(); } - private int fillMaterialEmptyRow(Sheet sheet, int rowNum, int startCol, int endCol, int fillRowNum, CellStyle style) { - while (rowNum < fillRowNum) { + private int fillMaterialEmptyRow(Sheet sheet, int dataSize, int startRowNum, int targetSize) { + int rowNum = startRowNum; + int fillRows = targetSize - dataSize; + + int sortOrder = dataSize + 1; + while (fillRows > 0) { Row dataRow = sheet.getRow(rowNum); if (dataRow == null) { - dataRow = sheet.createRow(rowNum); + dataRow = sheet.createRow(rowNum++); } - for (int i = startCol; i <= endCol - 1; i++) { + createNumericCell(dataRow, 1, Double.valueOf(sortOrder), rdLeftDataStyle); + sortOrder++; + + for (int i = 2; i <= 4; i++) { Double value = null; - createNumericCell(dataRow, i, value, style); + createCell(dataRow, i, "", rdDataStyle); } - Cell amountCell = dataRow.createCell(endCol); - amountCell.setCellFormula(String.format("D%d*E%d/10000", rowNum + 1, rowNum + 1)); - amountCell.setCellStyle(formulaStyle); + for (int i = 5; i <= 6; i++) { + Double value = null; + createNumericCell(dataRow, i, value, rdDataStyle); + } + + Cell amountCell = dataRow.createCell(7); + amountCell.setCellFormula(String.format("F%d*G%d", rowNum, rowNum)); + amountCell.setCellStyle(rdFormulaStyle); + + createCell(dataRow, 8, "", rdRightDataStyle); + + fillRows--; - rowNum++; } return rowNum; } @@ -664,25 +330,24 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { private int createMaterialSheetHeader(Sheet sheet, int startRowNum) { int rowNum = startRowNum; - // 表头 + // TITLE Row row1 = sheet.createRow(rowNum++); - createMergedCell(sheet, row1, 0, 5, "表三", titleStyle); - - Row row2 = sheet.createRow(rowNum++); - createMergedCell(sheet, row2, 0, 5, "材料费预算明细表", titleStyle); + createMergedCell(sheet, row1, 1, 8, "材料费预算明细表(表2)", rdTitleStyle); // 表头 Row headerRow = sheet.createRow(rowNum++); - createCell(headerRow, 3, "单价", headerStyle); + createCell(headerRow, 6, "单价", rdNoBottomBorderHeaderStyle); Row headerRow1 = sheet.createRow(rowNum++); - createCell(headerRow1, 3, "(元/单位数量)", headerStyle); + createCell(headerRow1, 6, "(万元)", rdNoTopBorderHeaderStyle); - createVerticalMergedCell(sheet, 2, 3, 0, "序号", headerStyle); - createVerticalMergedCell(sheet, 2, 3, 1, "材料名称", headerStyle); - createVerticalMergedCell(sheet, 2, 3, 2, "单位", headerStyle); - createVerticalMergedCell(sheet, 2, 3, 4, "购置数量", headerStyle); - createVerticalMergedCell(sheet, 2, 3, 5, "金额(万元)", headerStyle); + createVerticalMergedCell(sheet, 1, 2, 1, "序号", rdLeftHeaderStyle); + createVerticalMergedCell(sheet, 1, 2, 2, "材料类型", rdHeaderStyle); + createVerticalMergedCell(sheet, 1, 2, 3, "材料名称", rdHeaderStyle); + createVerticalMergedCell(sheet, 1, 2, 4, "单位", rdHeaderStyle); + createVerticalMergedCell(sheet, 1, 2, 5, "购置数量", rdHeaderStyle); + createVerticalMergedCell(sheet, 1, 2, 7, "总金额(万元)", rdHeaderStyle); + createVerticalMergedCell(sheet, 1, 2, 8, "预算价格依据", rdRightHeaderStyle); return rowNum; } @@ -707,18 +372,21 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { private int createMaterialDataRow(Sheet sheet, ErpRdBudgetMaterialCost detail, int rowNum) { Row dataRow = sheet.createRow(rowNum++); - createCell(dataRow, 0, detail.getSortOrder().toString(), dataStyle); - createCell(dataRow, 1, detail.getMaterialName(), dataStyle); - createCell(dataRow, 2, detail.getUnitName(), dataStyle); - Double unitPrice = detail.getUnitPrice() != null ? detail.getUnitPrice().doubleValue() : null; - createNumericCell(dataRow, 3, unitPrice, dataStyle); + createCell(dataRow, 1, detail.getSortOrder().toString(), rdLeftDataStyle); + createCell(dataRow, 2, detail.getMaterialType(), rdDataStyle); + createCell(dataRow, 3, detail.getMaterialName(), rdDataStyle); + createCell(dataRow, 4, detail.getUnitName(), rdDataStyle); Double amount = detail.getAmount() != null ? detail.getAmount().doubleValue() : null; - createNumericCell(dataRow, 4, amount, dataStyle); + createNumericCell(dataRow, 5, amount, rdDataStyle); + Double unitPrice = detail.getUnitPrice() != null ? detail.getUnitPrice().doubleValue() : null; + createNumericCell(dataRow, 6, unitPrice, rdDataStyle); // 设置金额公式 - Cell amountCell = dataRow.createCell(5); - amountCell.setCellFormula("D" + (rowNum) + "*E" + (rowNum) + "/10000"); - amountCell.setCellStyle(formulaStyle); + Cell amountCell = dataRow.createCell(7); + amountCell.setCellFormula("F" + (rowNum) + "*G" + (rowNum)); + amountCell.setCellStyle(rdFormulaStyle); + + createCell(dataRow, 8, detail.getRemark(), rdRightDataStyle); return rowNum; } @@ -726,40 +394,22 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { /** * 创建材料费小计行 */ - private int createMaterialSubtotalRows(Sheet sheet, int dataStartRow, int startRowNum, List otherMaterialCostList) { + private int createMaterialTotalRows(Sheet sheet, int dataStartRow, int startRowNum) { int rowNum = startRowNum; - ErpRdBudgetMaterialCost otherMaterialCost = !otherMaterialCostList.isEmpty() ? otherMaterialCostList.get(0) : new ErpRdBudgetMaterialCost(); - - // 主要材料费小计 - Row subtotalRow1 = sheet.createRow(rowNum++); - createMergedCell(sheet, subtotalRow1, 0, 1, "主要材料费小计", headerStyle); - createCell(subtotalRow1, 2, "/", headerStyle); - createCell(subtotalRow1, 3, "/", headerStyle); - createCell(subtotalRow1, 4, "/", headerStyle); - - Cell subtotalCell1 = subtotalRow1.createCell(5); - subtotalCell1.setCellFormula(String.format("SUM(F%d:F%d)", dataStartRow, rowNum - 1)); - subtotalCell1.setCellStyle(formulaStyle); - - // 其他材料费 - Row subtotalRow2 = sheet.createRow(rowNum++); - createMergedCell(sheet, subtotalRow2, 0, 1, "其他材料费", headerStyle); - createCell(subtotalRow2, 2, "/", headerStyle); - createCell(subtotalRow2, 3, "/", headerStyle); - createCell(subtotalRow2, 4, "/", headerStyle); - createNumericCell(subtotalRow2, 5, otherMaterialCost.getPrice() == null ? null : otherMaterialCost.getPrice().divide(new BigDecimal(10000)), moneyStyle); - // 合计 Row totalRow = sheet.createRow(rowNum); - createMergedCell(sheet, totalRow, 0, 1, "合计", headerStyle); - createCell(totalRow, 2, "/", headerStyle); - createCell(totalRow, 3, "/", headerStyle); - createCell(totalRow, 4, "/", headerStyle); + totalRow.setHeightInPoints(30); + createMergedCell(sheet, totalRow, 1, 3, "合计", rdLeftFooterStyle); + createCell(totalRow, 4, "", rdFooterStyle); + createCell(totalRow, 5, "", rdFooterStyle); + createCell(totalRow, 6, "", rdFooterStyle); - Cell totalCell = totalRow.createCell(5); - totalCell.setCellFormula(String.format("SUM(F%d:F%d)", rowNum - 1, rowNum)); - totalCell.setCellStyle(formulaStyle); + Cell totalCell = totalRow.createCell(7); + totalCell.setCellFormula(String.format("SUM(H%d:H%d)", dataStartRow, rowNum)); + totalCell.setCellStyle(rdFooterFormulaStyle); + + createCell(totalRow, 8, "", rdRightFooterStyle); return rowNum; } @@ -768,876 +418,119 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { * 设置材料费表列宽 */ private void setMaterialSheetColumnWidths(Sheet sheet) { - setColumnWidths(sheet, new int[]{20, 30, 30, 30, 30, 30}); + setColumnWidths(sheet, new int[]{5, 10, 15, 20, 10, 15, 18, 18, 20}); } - // =========== 差旅费、会议费、国际交流费明细表相关方法 =========== + private void createMaterialInstruction(Sheet sheet, int rowNum) { + int startRow = rowNum + 2; + int endRow = startRow; + String noteText = "填写说明:指研究开发活动过程中投入的各种半成品、原材料、辅助材料等费用。(半成品:包括自制半成品、外协加工件。原材料:外购件、带料外协的外购件。辅助材料:现场使用的耗材)"; + List keywords = Arrays.asList("填写说明:"); - /** - * 创建差旅费、会议费、国际交流费明细表 - */ - private Integer createTravelMeetingSheet(ErpBudgetInfoVo budget) { - Sheet sheet = workbook.createSheet(SHEET_TRAVEL_MEETING); - int rowNum = 0; - - // 创建差旅费部分 - rowNum = createTravelSection(sheet, budget, rowNum); - addTotalRowMapping(BUDGET_TRAVEL, rowNum); - - // 创建会议费部分 - rowNum = createMeetingSection(sheet, budget, rowNum); - addTotalRowMapping(BUDGET_MEETING, rowNum); - - // 创建国际交流费部分 - rowNum = createInternationalSection(sheet, budget, rowNum); - addTotalRowMapping(BUDGET_INTERNATIONAL, rowNum + 1); - - // 设置列宽 - setTravelMeetingSheetColumnWidths(sheet); - - return rowNum; + createMergedCellWithBoldKeywords(sheet, startRow, endRow, 1, 8, + noteText, keywords, rdInstructionStyle, 30); } - /** - * 创建差旅费部分 - */ - private int createTravelSection(Sheet sheet, ErpBudgetInfoVo budget, int startRowNum) { - int rowNum = startRowNum; - // 表头 - Row row1 = sheet.createRow(rowNum++); - createMergedCell(sheet, row1, 0, 11, "表四", titleStyle); - - Row row2 = sheet.createRow(rowNum++); - createMergedCell(sheet, row2, 0, 11, "差旅费预算明细表", titleStyle); - - Row travelHeaderRow = sheet.createRow(rowNum++); - createCell(travelHeaderRow, 0, "差旅费", headerStyle); - String[] travelHeaders = {"序号", "出差地点", "事由", "次数", "人数", "天数", "住宿标准(元)", "往返路费(元)", "住宿费(元)", "补贴(元)", "小计(万元)"}; - for (int i = 0; i < travelHeaders.length; i++) { - createCell(travelHeaderRow, i + 1, travelHeaders[i], headerStyle); - } - - // 数据行 - int travelDataStartRow = rowNum + 1; - rowNum = createTravelDataRows(sheet, budget, rowNum); - - // 填充空行 - rowNum = createEmptyTravelRow(sheet, rowNum, budget); - - // 差旅费合计 - rowNum = createTravelTotalRow(sheet, travelDataStartRow, rowNum); - - // 创建左侧合并单元格 - createDataRowsWithDynamicMerge(sheet, 2, rowNum - 2, 0, 0, "差旅费"); - - return rowNum; - } + // =========== 人工费=========== /** - * 创建差旅费数据行 - */ - private int createTravelDataRows(Sheet sheet, ErpBudgetInfoVo budget, int startRowNum) { - int rowNum = startRowNum; - - if (budget.getErpRdBudgetTravelCostList() != null) { - for (int i = 0; i < budget.getErpRdBudgetTravelCostList().size(); i++) { - ErpRdBudgetTravelCost detail = budget.getErpRdBudgetTravelCostList().get(i); - rowNum = createTravelDataRow(sheet, detail, rowNum); - } - } - - return rowNum; - } - - /** - * 创建单个差旅费数据行 - */ - private int createTravelDataRow(Sheet sheet, ErpRdBudgetTravelCost detail, int rowNum) { - Row dataRow = sheet.createRow(rowNum++); - - createCell(dataRow, 0, "", dataStyle); - createCell(dataRow, 1, detail.getSortOrder().toString(), dataStyle); - createCell(dataRow, 2, detail.getTripLocation(), dataStyle); - createCell(dataRow, 3, detail.getReason(), dataStyle); - createNumericCell(dataRow, 4, detail.getFrequency() == null ? null : detail.getFrequency().doubleValue(), dataStyle); - createNumericCell(dataRow, 5, detail.getPeopleNumber() == null ? null : detail.getPeopleNumber().doubleValue(), dataStyle); - createNumericCell(dataRow, 6, detail.getDays(), dataStyle); - createNumericCell(dataRow, 7, detail.getStayStandard(), dataStyle); - createNumericCell(dataRow, 8, detail.getTravelExpenses(), dataStyle); - - // 公式计算 - createTravelFormulas(dataRow, rowNum); - - return rowNum; - } - - /** - * 创建差旅费公式 - */ - private void createTravelFormulas(Row row, int rowNum) { - Cell accommodationCell = row.createCell(9); - accommodationCell.setCellFormula(String.format("H%d*G%d*F%d", rowNum, rowNum, rowNum)); - accommodationCell.setCellStyle(formulaStyle); - - Cell subsidyCell = row.createCell(10); - subsidyCell.setCellFormula(String.format("F%d*G%d*90", rowNum, rowNum)); - subsidyCell.setCellStyle(formulaStyle); - - Cell subtotalCell = row.createCell(11); - subtotalCell.setCellFormula("SUM(I" + rowNum + ":K" + rowNum + ")/10000"); - subtotalCell.setCellStyle(formulaStyle); - } - - /** - * 创建空差旅费行 - */ - private int createEmptyTravelRow(Sheet sheet, int rowNum, ErpBudgetInfoVo budget) { - List erpRdBudgetTravelCostList = budget.getErpRdBudgetTravelCostList() == null ? new ArrayList<>() : - budget.getErpRdBudgetTravelCostList(); - - int rdBudgetTravelFillRowNum = rowNum + (7 - erpRdBudgetTravelCostList.size()); - while (rowNum <= rdBudgetTravelFillRowNum) { - Row emptyRow = sheet.createRow(rowNum++); - for (int i = 1; i < 4; i++) { - createCell(emptyRow, i, "", dataStyle); - } - for (int i = 4; i < 12; i++) { - Double value = null; - createNumericCell(emptyRow, i, value, dataStyle); - } - createTravelFormulas(emptyRow, rowNum); - } - - return rowNum; - } - - /** - * 创建差旅费合计行 - */ - private int createTravelTotalRow(Sheet sheet, int dataStartRow, int rowNum) { - Row travelTotalRow = sheet.createRow(rowNum++); - - createMergedCell(sheet, travelTotalRow, 1, 7, "合计", headerStyle); - createCell(travelTotalRow, 8, "", headerStyle); - createCell(travelTotalRow, 9, "", headerStyle); - createCell(travelTotalRow, 10, "", headerStyle); - - Cell travelTotalCell = travelTotalRow.createCell(11); - travelTotalCell.setCellFormula(String.format("SUM(L%d:L%d)", dataStartRow, rowNum - 1)); - travelTotalCell.setCellStyle(formulaStyle); - - return rowNum; - } - - /** - * 创建会议费部分 - */ - private int createMeetingSection(Sheet sheet, ErpBudgetInfoVo budget, int startRowNum) { - int rowNum = startRowNum; - - // 会议费部分 - Row meetingTitleRow = sheet.createRow(rowNum++); - createMergedCell(sheet, meetingTitleRow, 0, 11, "会议费预算明细表", titleStyle); - - int meetingStartRow = rowNum; - - Row meetingHeaderRow1 = sheet.createRow(rowNum++); - createMergedCell(sheet, meetingHeaderRow1, 1, 8, "公司组织会议", headerStyle); - createMergedCell(sheet, meetingHeaderRow1, 9, 10, "外出参加会议", headerStyle); - - Row meetingHeaderRow2 = sheet.createRow(rowNum++); - createCell(meetingHeaderRow2, 0, "会议费", headerStyle); - String[] meetingHeaders = {"序号", "会议内容", "", "", "场地日租金(元)", "日均杂费(元)", "天数", "专家交通住宿费", "人数", "人均交费", ""}; - for (int i = 0; i < meetingHeaders.length; i++) { - createCell(meetingHeaderRow2, i + 1, meetingHeaders[i], headerStyle); - } - - // 合并单元格 - createMergedCell(sheet, meetingHeaderRow2, 2, 4, "会议内容", headerStyle); - createVerticalMergedCell(sheet, meetingStartRow, meetingStartRow + 1, 11, "会议费(万元)", headerStyle); - - int meetingDataStartRow = rowNum + 1; - // 数据行 - rowNum = createMeetingDataRows(sheet, budget, rowNum); - - // 填充空行 - rowNum = createEmptyMeetingRow(sheet, rowNum, budget); - - // 会议费合计 - rowNum = createMeetingTotalRow(sheet, meetingDataStartRow, rowNum); - - // 创建左侧合并单元格 - createDataRowsWithDynamicMerge(sheet, meetingStartRow, rowNum - meetingStartRow, 0, 0, "会议费"); - - - return rowNum; - } - - /** - * 创建会议费数据行 - */ - private int createMeetingDataRows(Sheet sheet, ErpBudgetInfoVo budget, int startRowNum) { - int rowNum = startRowNum; - -// if (budget.getErpRdBudgetMeetingCostList() != null) { -// for (int i = 0; i < budget.getErpRdBudgetMeetingCostList().size(); i++) { -// ErpRdBudgetMeetingCost detail = budget.getErpRdBudgetMeetingCostList().get(i); -// rowNum = createMeetingDataRow(sheet, detail, rowNum); -// } -// } - - return rowNum; - } - - /** - * 创建单个会议费数据行 - */ - private int createMeetingDataRow(Sheet sheet, ErpRdBudgetMeetingCost detail, int rowNum) { - Row dataRow = sheet.createRow(rowNum++); - - createCell(dataRow, 0, "", dataStyle); - createCell(dataRow, 1, detail.getSortOrder().toString(), dataStyle); - createMergedCell(sheet, dataRow, 2, 4, detail.getMeetingContent(), dataStyle); - Double rentalFee = detail.getRentalFee() != null ? detail.getRentalFee().doubleValue() : null; - createNumericCell(dataRow, 5, rentalFee, dataStyle); - Double dailyExpense = detail.getDailyExpense() != null ? detail.getDailyExpense().doubleValue() : null; - createNumericCell(dataRow, 6, dailyExpense, dataStyle); - Double days = detail.getDays() != null ? detail.getDays().doubleValue() : null; - createNumericCell(dataRow, 7, days, dataStyle); - Double expertExpense = detail.getExpertExpense() != null ? detail.getExpertExpense().doubleValue() : null; - createNumericCell(dataRow, 8, expertExpense, dataStyle); - Double peopleNumber = detail.getPeopleNumber() == null ? null : detail.getPeopleNumber().doubleValue(); - createNumericCell(dataRow, 9, peopleNumber, dataStyle); - Double perPersonExpense = detail.getPerPersonExpense() != null ? detail.getPerPersonExpense().doubleValue() : null; - createNumericCell(dataRow, 10, perPersonExpense, dataStyle); - - // 会议费公式 - Cell meetingCostCell = dataRow.createCell(11); - meetingCostCell.setCellFormula("ROUND(((N(F" + rowNum + ")+N(G" + rowNum + "))*N(H" + rowNum + - ")+N(I" + rowNum + - ")+N(J" + rowNum + ")*N(K" + rowNum + ")" + - ")/10000,2)"); - meetingCostCell.setCellStyle(formulaStyle); - - return rowNum; - } - - /** - * 创建空会议费行 - */ - private int createEmptyMeetingRow(Sheet sheet, int rowNum, ErpBudgetInfoVo budget) { -// List erpRdBudgetMeetingCostList = budget.getErpRdBudgetMeetingCostList() == null ? new ArrayList<>() : -// budget.getErpRdBudgetMeetingCostList(); -// -// int rdBudgetMeetingFillRowNum = rowNum + (4 - erpRdBudgetMeetingCostList.size()); -// while (rowNum <= rdBudgetMeetingFillRowNum) { -// Row emptyRow = sheet.createRow(rowNum++); -// createCell(emptyRow, 1, "", dataStyle); -// createMergedCell(sheet, emptyRow, 2, 4, "", dataStyle); -// for (int i = 5; i < 11; i++) { -// Double value = null; -// createNumericCell(emptyRow, i, value, dataStyle); -// } -// Cell meetingCostCell = emptyRow.createCell(11); -// meetingCostCell.setCellFormula("ROUND(((F" + (rowNum) + "+G" + (rowNum) + ")*H" + (rowNum) + "+I" + (rowNum) + "+J" + (rowNum) + "*K" + (rowNum) + ")/10000,2)"); -// meetingCostCell.setCellStyle(formulaStyle); -// } - - return rowNum; - } - - /** - * 创建会议费合计行 - */ - private int createMeetingTotalRow(Sheet sheet, int dataStartRow, int rowNum) { - Row meetingTotalRow = sheet.createRow(rowNum++); - - createMergedCell(sheet, meetingTotalRow, 1, 10, "合计", headerStyle); - - Cell meetingTotalCell = meetingTotalRow.createCell(11); - meetingTotalCell.setCellFormula(String.format("SUM(L%d:L%d)", dataStartRow, rowNum - 1)); - meetingTotalCell.setCellStyle(formulaStyle); - - return rowNum; - } - - /** - * 创建国际交流费部分 - */ - private int createInternationalSection(Sheet sheet, ErpBudgetInfoVo budget, int startRowNum) { - int rowNum = startRowNum; - - // 国际交流费部分 - Row internationalTitleRow = sheet.createRow(rowNum++); - createMergedCell(sheet, internationalTitleRow, 0, 11, "国际交流费预算明细表", titleStyle); - - int internationalStartRow = rowNum; - - Row internationalHeaderRow = sheet.createRow(rowNum++); - createCell(internationalHeaderRow, 0, "国际合作与交流费", headerStyle); - String[] internationalHeaders = {"序号", "合作交流类型", "国家和地区", "", "机构", "", "人数(人)", "时间(天)", "往返路费及住宿费", "补贴(元)", "小计(万元)"}; - for (int i = 0; i < internationalHeaders.length; i++) { - createCell(internationalHeaderRow, i + 1, internationalHeaders[i], headerStyle); - } - - // 合并单元格 - createMergedCell(sheet, internationalHeaderRow, 3, 4, "国家和地区", headerStyle); - createMergedCell(sheet, internationalHeaderRow, 5, 6, "机构", headerStyle); - - int internationalDataStartRow = rowNum + 1; - // 数据行 - rowNum = createInternationalDataRows(sheet, budget, rowNum); - - // 填充空行 - rowNum = createEmptyInternationalRow(sheet, rowNum, budget); - - // 国际交流费合计 - rowNum = createInternationalTotalRow(sheet, internationalDataStartRow, rowNum); - - // 创建左侧合并单元格 - createDataRowsWithDynamicMerge(sheet, internationalStartRow, rowNum - internationalStartRow + 1, 0, 0, "国际合作与交流费"); - - return rowNum; - } - - /** - * 创建国际交流费数据行 - */ - private int createInternationalDataRows(Sheet sheet, ErpBudgetInfoVo budget, int startRowNum) { - int rowNum = startRowNum; - -// if (budget.getErpRdBudgetExchangeCostList() != null) { -// for (int i = 0; i < budget.getErpRdBudgetExchangeCostList().size(); i++) { -// ErpRdBudgetExchangeCost detail = budget.getErpRdBudgetExchangeCostList().get(i); -// rowNum = createInternationalDataRow(sheet, detail, rowNum); -// } -// } - - return rowNum; - } - - /** - * 创建单个国际交流费数据行 - */ - private int createInternationalDataRow(Sheet sheet, ErpRdBudgetExchangeCost detail, int rowNum) { - Row dataRow = sheet.createRow(rowNum++); - - createCell(dataRow, 0, "", dataStyle); - createCell(dataRow, 1, detail.getSortOrder().toString(), dataStyle); - createCell(dataRow, 2, detail.getCommunicationType(), dataStyle); - createMergedCell(sheet, dataRow, 3, 4, detail.getCountryRegion(), dataStyle); - createMergedCell(sheet, dataRow, 5, 6, detail.getInstitution(), dataStyle); - createNumericCell(dataRow, 7, detail.getPeopleNumber() == null ? null : detail.getPeopleNumber().doubleValue(), dataStyle); - createNumericCell(dataRow, 8, detail.getDays(), dataStyle); - createNumericCell(dataRow, 9, detail.getTravelAccommodationExpense(), dataStyle); - - // 补贴公式 - Cell subsidyCell = dataRow.createCell(10); - subsidyCell.setCellFormula("IF(C" + (rowNum) + "=\"出国考察\",40*I" + (rowNum) + "*H" + (rowNum) + ",0)"); - subsidyCell.setCellStyle(formulaStyle); - - // 小计公式 - Cell subtotalCell = dataRow.createCell(11); - subtotalCell.setCellFormula("ROUND((K" + (rowNum) + "+J" + (rowNum) + ")/10000,2)"); - subtotalCell.setCellStyle(formulaStyle); - - return rowNum; - } - - /** - * 创建空国际交流费行 - */ - private int createEmptyInternationalRow(Sheet sheet, int rowNum, ErpBudgetInfoVo budget) { -// List erpRdBudgetExchangeCostList = budget.getErpRdBudgetExchangeCostList() == null ? new ArrayList<>() : -// budget.getErpRdBudgetExchangeCostList(); -// -// int rdBudgetInternationalFillRowNum = rowNum + (4 - erpRdBudgetExchangeCostList.size()); -// while (rowNum <= rdBudgetInternationalFillRowNum) { -// Row emptyRow = sheet.createRow(rowNum++); -// createCell(emptyRow, 1, "", dataStyle); -// createCell(emptyRow, 2, "", dataStyle); -// createMergedCell(sheet, emptyRow, 3, 4, "", dataStyle); -// createMergedCell(sheet, emptyRow, 5, 6, "", dataStyle); -// for (int i = 7; i < 10; i++) { -// Double value = null; -// createNumericCell(emptyRow, i, value, dataStyle); -// } -// Cell subsidyCell = emptyRow.createCell(10); -// subsidyCell.setCellFormula("IF(C" + (rowNum) + "=\"出国考察\",40*I" + (rowNum) + "*H" + (rowNum) + ",0)"); -// subsidyCell.setCellStyle(formulaStyle); -// -// Cell subtotalCell = emptyRow.createCell(11); -// subtotalCell.setCellFormula("ROUND((K" + (rowNum) + "+J" + (rowNum) + ")/10000,2)"); -// subtotalCell.setCellStyle(formulaStyle); -// } - - return rowNum; - } - - /** - * 创建国际交流费合计行 - */ - private int createInternationalTotalRow(Sheet sheet, int internationalDataStartRow, int rowNum) { - Row internationalTotalRow = sheet.createRow(rowNum); - - createMergedCell(sheet, internationalTotalRow, 1, 10, "合计", headerStyle); - - Cell internationalTotalCell = internationalTotalRow.createCell(11); - internationalTotalCell.setCellFormula(String.format("SUM(L%d:L%d)", internationalDataStartRow, rowNum)); - internationalTotalCell.setCellStyle(formulaStyle); - - return rowNum; - } - - /** - * 设置差旅会议交流表列宽 - */ - private void setTravelMeetingSheetColumnWidths(Sheet sheet) { - setColumnWidths(sheet, new int[]{20, 15, 20, 20, 15, 20, 15, 20, 20, 20, 20, 20, 20}); - } - - // =========== 人工费、劳务费、咨询开发费明细表相关方法 =========== - - /** - * 创建人工费、劳务费、咨询开发费明细表 + * 创建人工费明细表 */ private Integer createLaborServiceSheet(ErpBudgetInfoVo budget) { - Sheet sheet = workbook.createSheet(SHEET_LABOR_SERVICE); - + Sheet sheet = workbook.createSheet(RdBudgetItemEnums.LABOR.getSheetName()); + int rowNum = 0; // 准备数据 - List budgetTechCostList = prepareTechCostData(budget); List budgetLaborCostList = prepareLaborCostData(budget); - int rowNum = 0; + // 创建表头 + rowNum = createLaborSheetHeader(sheet, rowNum); - // 创建技术服务费部分 - rowNum = createTechConsultSection(sheet, budgetTechCostList, rowNum); - addTotalRowMapping(BUDGET_TECH, rowNum); + // 创建数据行 + int dataStartRow = rowNum + 1; + rowNum = createLaborDataRows(sheet, budgetLaborCostList, rowNum); - // 创建人工费部分 - rowNum = createLaborCostSection(sheet, budgetLaborCostList, rowNum); - addTotalRowMapping(BUDGET_LABOR, rowNum); + // 填充空行 + rowNum = fillLaborCostEmptyRows(sheet, budgetLaborCostList.size(), rowNum, 6); - // 创建劳务费部分 - rowNum = createServiceCostSection(sheet, budgetLaborCostList, rowNum); - addTotalRowMapping(BUDGET_SERVICE, rowNum + 1); + // 人工费合计 + rowNum = createLaborCostTotalRow(sheet, dataStartRow, rowNum); // 设置列宽 setLaborServiceSheetColumnWidths(sheet); + //设置说明 + createLaborInstruction(sheet, rowNum); return rowNum; } /** - * 准备技术服务费数据 - */ - private List prepareTechCostData(ErpBudgetInfoVo budget) { - List budgetTechCostList = budget.getErpRdBudgetTechCostList() == null ? - new ArrayList<>() : budget.getErpRdBudgetTechCostList(); - return budgetTechCostList; - } - - /** - * 准备人工费劳务费数据 + * 准备人工费数据 */ private List prepareLaborCostData(ErpBudgetInfoVo budget) { - List budgetLaborCostList = budget.getErpRdBudgetLaborCostList() == null ? + return budget.getErpRdBudgetLaborCostList() == null ? new ArrayList<>() : budget.getErpRdBudgetLaborCostList(); - return budgetLaborCostList; - } - - /** - * 创建技术服务费部分 - */ - private int createTechConsultSection(Sheet sheet, List budgetTechCostList, int startRowNum) { - int rowNum = startRowNum; - - // 表头 - Row row1 = sheet.createRow(rowNum++); - createMergedCell(sheet, row1, 0, 8, "表五", titleStyle); - - Row row2 = sheet.createRow(rowNum++); - createMergedCell(sheet, row2, 0, 8, "技术服务费预算明细表", titleStyle); - - // 分组数据 - List techConsultList = filterTechCostByType(budgetTechCostList, TechTypeEnum.TECH_CONSULT.getCode()); - List expertMeetingList = filterTechCostByType(budgetTechCostList, TechTypeEnum.EXPERT_MEETING.getCode()); - List expertCommunicationList = filterTechCostByType(budgetTechCostList, TechTypeEnum.EXPERT_COMMUNICATION.getCode()); - - // 技术咨询开发部分 - int techConsultStartRow = rowNum; - rowNum = createTechConsultSubsection(sheet, techConsultList, rowNum, "技术咨询开发", 1); - int techConsultTotalRowNum = rowNum; - - // 专家咨询-会议形式部分 - int expertMeetingStartRow = rowNum; - rowNum = createExpertMeetingSubsection(sheet, expertMeetingList, rowNum); - int expertMeetingTotalRowNum = rowNum; - - // 专家咨询-通讯形式部分 - int expertCommunicationStartRow = rowNum; - rowNum = createExpertCommunicationSubsection(sheet, expertCommunicationList, rowNum); - int expertCommunicationTotalRowNum = rowNum; - - // 合并左侧单元格 - createVerticalMergedCell(sheet, expertMeetingStartRow, rowNum - 1, 1, "专家咨询", leftMergeStyle); - createVerticalMergedCell(sheet, techConsultStartRow, rowNum, 0, "咨询、开发费", leftMergeStyle); - - // 技术咨询-合计 - rowNum = createTechTotalRow(sheet, techConsultTotalRowNum, expertMeetingTotalRowNum, expertCommunicationTotalRowNum, rowNum); - - return rowNum; - } - - /** - * 按类型筛选技术服务费数据 - */ - private List filterTechCostByType(List list, String type) { - return list.stream() - .filter(item -> item.getTechType().equals(type)) - .toList(); - } - - /** - * 创建技术咨询开发子部分 - */ - private int createTechConsultSubsection(Sheet sheet, List techConsultList, int startRowNum, String label, int labelCol) { - int rowNum = startRowNum; - - Row serviceHeaderRow1 = sheet.createRow(rowNum++); - createMergedCell(sheet, serviceHeaderRow1, 2, 5, "内容", headerStyle); - createCell(serviceHeaderRow1, 6, "单位", headerStyle); - createCell(serviceHeaderRow1, 7, "备注", headerStyle); - createCell(serviceHeaderRow1, 8, "金额(万元)", headerStyle); - - // 技术咨询开发数据 - int dataStartRow = rowNum + 1; - for (ErpRdBudgetTechCost detail : techConsultList) { - rowNum = createTechConsultDataRow(sheet, detail, rowNum); - } - - // 填充空行 - rowNum = fillTechConsultEmptyRows(sheet, techConsultList.size(), rowNum, 3); - - // 小计行 - rowNum = createTechConsultSubtotalRow(sheet, dataStartRow, rowNum); - - // 创建左侧合并单元格 - createVerticalMergedCell(sheet, startRowNum, rowNum - 1, labelCol, label, leftMergeStyle); - - return rowNum; - } - - /** - * 创建技术咨询开发数据行 - */ - private int createTechConsultDataRow(Sheet sheet, ErpRdBudgetTechCost detail, int rowNum) { - Row dataRow = sheet.createRow(rowNum++); - - createMergedCell(sheet, dataRow, 2, 5, detail.getTechContent(), dataStyle); - createCell(dataRow, 6, detail.getUnitName(), dataStyle); - createCell(dataRow, 7, detail.getRemark(), dataStyle); - Double price = detail.getPrice() != null ? detail.getPrice().doubleValue() : null; - createNumericCell(dataRow, 8, price, dataStyle); - - return rowNum; - } - - /** - * 填充技术咨询开发空行 - */ - private int fillTechConsultEmptyRows(Sheet sheet, int dataSize, int startRowNum, int targetSize) { - int rowNum = startRowNum; - int fillRows = targetSize - dataSize; - - while (fillRows > 0) { - Row emptyRow = sheet.createRow(rowNum++); - createMergedCell(sheet, emptyRow, 2, 5, "", dataStyle); - createCell(emptyRow, 6, "", dataStyle); - createCell(emptyRow, 7, "", dataStyle); - createCell(emptyRow, 8, "", dataStyle); - fillRows--; - } - - return rowNum; - } - - /** - * 创建技术咨询开发小计行 - */ - private int createTechConsultSubtotalRow(Sheet sheet, int dataStartRow, int rowNum) { - Row totalRow = sheet.createRow(rowNum++); - - createMergedCell(sheet, totalRow, 2, 7, "小计", headerStyle); - - Cell totalCell = totalRow.createCell(8); - totalCell.setCellFormula(String.format("SUM(I%d:I%d)", dataStartRow, rowNum - 1)); - totalCell.setCellStyle(formulaStyle); - - return rowNum; - } - - /** - * 创建专家咨询-会议形式子部分 - */ - private int createExpertMeetingSubsection(Sheet sheet, List expertMeetingList, int startRowNum) { - int rowNum = startRowNum; - - Row expertHeaderRow = sheet.createRow(rowNum++); - createMergedCell(sheet, expertHeaderRow, 3, 4, "内容", headerStyle); - createCell(expertHeaderRow, 5, "专家人数", headerStyle); - createCell(expertHeaderRow, 6, "", headerStyle); - createCell(expertHeaderRow, 7, "天数", headerStyle); - createCell(expertHeaderRow, 8, "金额(万元)", headerStyle); - - // 专家咨询-会议形式数据 - int dataStartRow = rowNum + 1; - for (ErpRdBudgetTechCost detail : expertMeetingList) { - rowNum = createExpertMeetingDataRow(sheet, detail, rowNum); - } - - // 填充空行 - rowNum = fillExpertMeetingEmptyRows(sheet, expertMeetingList.size(), rowNum, 3); - - // 小计行 - rowNum = createExpertMeetingSubtotalRow(sheet, dataStartRow, rowNum); - - // 创建左侧合并单元格 - createVerticalMergedCell(sheet, startRowNum, rowNum - 1, 2, "会议形式", leftMergeStyle); - - return rowNum; - } - - /** - * 创建专家咨询-会议形式数据行 - */ - private int createExpertMeetingDataRow(Sheet sheet, ErpRdBudgetTechCost detail, int rowNum) { - Row dataRow = sheet.createRow(rowNum++); - - createMergedCell(sheet, dataRow, 3, 4, detail.getTechContent(), dataStyle); - Double peopleNumber = detail.getPeopleNumber() != null ? detail.getPeopleNumber().doubleValue() : null; - createNumericCell(dataRow, 5, peopleNumber, dataStyle); - createCell(dataRow, 6, "", dataStyle); - Double days = detail.getDays() != null ? detail.getDays().doubleValue() : null; - createNumericCell(dataRow, 7, days, dataStyle); - - Cell priceCell = dataRow.createCell(8); - priceCell.setCellFormula(String.format("ROUND(IF(H%d<2,H%d*F%d*600,(F%d*2*600+F%d*(H%d-2)*300))/10000,2)", rowNum, rowNum, rowNum, rowNum, rowNum, rowNum)); - priceCell.setCellStyle(formulaStyle); - - return rowNum; - } - - /** - * 填充专家咨询-会议形式空行 - */ - private int fillExpertMeetingEmptyRows(Sheet sheet, int dataSize, int startRowNum, int targetSize) { - int rowNum = startRowNum; - int fillRows = targetSize - dataSize; - - while (fillRows > 0) { - Row emptyRow = sheet.createRow(rowNum++); - createMergedCell(sheet, emptyRow, 3, 4, "", dataStyle); - createCell(emptyRow, 5, "", dataStyle); - createCell(emptyRow, 6, "", dataStyle); - createCell(emptyRow, 7, "", dataStyle); - createCell(emptyRow, 8, "", dataStyle); - fillRows--; - } - - return rowNum; - } - - /** - * 创建专家咨询-会议形式小计行 - */ - private int createExpertMeetingSubtotalRow(Sheet sheet, int dataStartRow, int rowNum) { - Row totalRow = sheet.createRow(rowNum++); - - createMergedCell(sheet, totalRow, 3, 7, "小计", headerStyle); - - Cell totalCell = totalRow.createCell(8); - totalCell.setCellFormula(String.format("SUM(I%d:I%d)", dataStartRow, rowNum - 1)); - totalCell.setCellStyle(formulaStyle); - - return rowNum; - } - - /** - * 创建专家咨询-通讯形式子部分 - */ - private int createExpertCommunicationSubsection(Sheet sheet, List expertCommunicationList, int startRowNum) { - int rowNum = startRowNum; - - Row expertCommunicationHeaderRow = sheet.createRow(rowNum++); - createMergedCell(sheet, expertCommunicationHeaderRow, 3, 4, "内容", headerStyle); - createCell(expertCommunicationHeaderRow, 5, "人数", headerStyle); - createCell(expertCommunicationHeaderRow, 6, "", headerStyle); - createCell(expertCommunicationHeaderRow, 7, "次数", headerStyle); - createCell(expertCommunicationHeaderRow, 8, "金额(万元)", headerStyle); - - // 专家咨询-通讯形式数据 - int dataStartRow = rowNum + 1; - for (ErpRdBudgetTechCost detail : expertCommunicationList) { - rowNum = createExpertCommunicationDataRow(sheet, detail, rowNum); - } - - // 填充空行 - rowNum = fillExpertCommunicationEmptyRows(sheet, expertCommunicationList.size(), rowNum, 3); - - // 小计行 - rowNum = createExpertCommunicationSubtotalRow(sheet, dataStartRow, rowNum); - - // 创建左侧合并单元格 - createVerticalMergedCell(sheet, startRowNum, rowNum - 1, 2, "通讯形式", leftMergeStyle); - - return rowNum; - } - - /** - * 创建专家咨询-通讯形式数据行 - */ - private int createExpertCommunicationDataRow(Sheet sheet, ErpRdBudgetTechCost detail, int rowNum) { - Row dataRow = sheet.createRow(rowNum++); - - createMergedCell(sheet, dataRow, 3, 4, detail.getTechContent(), dataStyle); - createNumericCell(dataRow, 5, detail.getPeopleNumber() == null ? null : detail.getPeopleNumber().doubleValue(), dataStyle); - createCell(dataRow, 6, "", dataStyle); - createNumericCell(dataRow, 7, detail.getFrequency() == null ? null : detail.getFrequency().doubleValue(), dataStyle); - - Cell priceCell = dataRow.createCell(8); - priceCell.setCellFormula(String.format("ROUND(F%d*H%d*70/10000,2)", rowNum, rowNum)); - priceCell.setCellStyle(formulaStyle); - - return rowNum; - } - - /** - * 填充专家咨询-通讯形式空行 - */ - private int fillExpertCommunicationEmptyRows(Sheet sheet, int dataSize, int startRowNum, int targetSize) { - int rowNum = startRowNum; - int fillRows = targetSize - dataSize; - - while (fillRows > 0) { - Row emptyRow = sheet.createRow(rowNum++); - createMergedCell(sheet, emptyRow, 3, 4, "", dataStyle); - createCell(emptyRow, 5, "", dataStyle); - createCell(emptyRow, 6, "", dataStyle); - createCell(emptyRow, 7, "", dataStyle); - createCell(emptyRow, 8, "", dataStyle); - fillRows--; - } - - return rowNum; - } - - /** - * 创建专家咨询-通讯形式小计行 - */ - private int createExpertCommunicationSubtotalRow(Sheet sheet, int dataStartRow, int rowNum) { - Row totalRow = sheet.createRow(rowNum++); - - createMergedCell(sheet, totalRow, 3, 7, "小计", headerStyle); - - Cell totalCell = totalRow.createCell(8); - totalCell.setCellFormula(String.format("SUM(I%d:I%d)", dataStartRow, rowNum - 1)); - totalCell.setCellStyle(formulaStyle); - - return rowNum; - } - - /** - * 创建技术服务费合计行 - */ - private int createTechTotalRow(Sheet sheet, int techConsultTotalRowNum, int expertMeetingTotalRowNum, - int expertCommunicationTotalRowNum, int rowNum) { - Row techTotalRow = sheet.createRow(rowNum++); - - createMergedCell(sheet, techTotalRow, 1, 7, "合计", headerStyle); - - Cell techTotalCell = techTotalRow.createCell(8); - techTotalCell.setCellFormula(String.format("I%d+I%d+I%d", techConsultTotalRowNum, expertMeetingTotalRowNum, expertCommunicationTotalRowNum)); - techTotalCell.setCellStyle(formulaStyle); - - return rowNum; } /** * 创建人工费部分 */ - private int createLaborCostSection(Sheet sheet, List budgetLaborCostList, int startRowNum) { + private int createLaborSheetHeader(Sheet sheet, int startRowNum) { int rowNum = startRowNum; - // 人工费部分标题 - Row laborTitleRow = sheet.createRow(rowNum++); - createMergedCell(sheet, laborTitleRow, 0, 9, "人工费预算明细表", titleStyle); + // TITLE + Row row1 = sheet.createRow(rowNum++); + createMergedCell(sheet, row1, 1, 8, "人工费预算明细表(表3-1)", rdTitleStyle); - int laborStartRowNum = rowNum; - - // 人工费表头 - Row laborHeaderRow = sheet.createRow(rowNum++); - createMergedCell(sheet, laborHeaderRow, 2, 3, "人员类别", headerStyle); - createCell(laborHeaderRow, 4, "人数", headerStyle); - createCell(laborHeaderRow, 5, "累计时间(月)", headerStyle); - createCell(laborHeaderRow, 6, "月平均投入比例(%)", headerStyle); - createCell(laborHeaderRow, 7, "人工标准(元/人月)", headerStyle); - createCell(laborHeaderRow, 8, "金额(万元)", headerStyle); - createCell(laborHeaderRow, 9, "投入人员", headerStyle); - - // 过滤人工费数据 - List laborCostList = filterLaborCostByType(budgetLaborCostList, LaborTypeEnum.LABOR_COST.getCode()); - - // 创建人工费数据行 - int dataStartRow = rowNum + 1; - for (ErpRdBudgetLaborCost detail : laborCostList) { - rowNum = createLaborCostDataRow(sheet, detail, rowNum); - } - - // 填充空行 - rowNum = fillLaborCostEmptyRows(sheet, laborCostList.size(), rowNum, 9); - - // 人工费合计 - rowNum = createLaborCostTotalRow(sheet, dataStartRow, rowNum); - - // 创建左侧合并单元格 - createDataRowsWithDynamicMerge(sheet, laborStartRowNum, rowNum - laborStartRowNum, 0, 1, "人工费"); + // 表头 + Row headerRow = sheet.createRow(rowNum++); + createCell(headerRow, 1, "序号", rdLeftHeaderStyle); + createCell(headerRow, 2, "人员类别", rdHeaderStyle); + createCell(headerRow, 3, "人数", rdHeaderStyle); + createCell(headerRow, 4, "累计时间(月)", rdHeaderStyle); + createCell(headerRow, 5, "人均投入比例", rdHeaderStyle); + createCell(headerRow, 6, "人工标准(元/月)", rdHeaderStyle); + createCell(headerRow, 7, "金额(万元)", rdHeaderStyle); + createCell(headerRow, 8, "备注", rdRightHeaderStyle); return rowNum; } - /** - * 按类型筛选人工费劳务费数据 - */ - private List filterLaborCostByType(List list, String type) { - return list.stream() - .filter(item -> item.getLaborType().equals(type)) - .toList(); + + private int createLaborDataRows(Sheet sheet, List budgetLaborCostList, int startRowNum) { + int rowNum = startRowNum; + + // 创建人工费数据行 + for (ErpRdBudgetLaborCost detail : budgetLaborCostList) { + rowNum = createLaborCostDataRow(sheet, detail, rowNum); + } + return rowNum; } + /** * 创建人工费数据行 */ private int createLaborCostDataRow(Sheet sheet, ErpRdBudgetLaborCost detail, int rowNum) { Row dataRow = sheet.createRow(rowNum++); - createMergedCell(sheet, dataRow, 2, 3, detail.getPersonnelCategory(), dataStyle); - createNumericCell(dataRow, 4, detail.getPeopleNumber() == null ? null : detail.getPeopleNumber().doubleValue(), dataStyle); - Double camulativeTime = detail.getCumulativeTime() != null ? detail.getCumulativeTime().doubleValue() : null; - createNumericCell(dataRow, 5, camulativeTime, dataStyle); + createCell(dataRow, 1, detail.getSortOrder().toString(), rdLeftDataStyle); + createCell(dataRow, 2, detail.getPersonnelCategory(), rdDataStyle); + createNumericCell(dataRow, 3, detail.getPeopleNumber() == null ? null : detail.getPeopleNumber().doubleValue(), rdDataStyle); + Double cumulativeTime = detail.getCumulativeTime() != null ? detail.getCumulativeTime().doubleValue() : null; + createNumericCell(dataRow, 4, cumulativeTime, rdDataStyle); Double monthRate = detail.getMonthRate() != null ? detail.getMonthRate().divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).doubleValue() : null; - createNumericCell(dataRow, 6, monthRate, percentStyle); + createNumericCell(dataRow, 5, monthRate, percentStyle); Double artificialStandard = detail.getArtificialStandard() != null ? detail.getArtificialStandard().doubleValue() : null; - createNumericCell(dataRow, 7, artificialStandard, dataStyle); + createNumericCell(dataRow, 6, artificialStandard, rdDataStyle); // 金额公式 - Cell amountCell = dataRow.createCell(8); - amountCell.setCellFormula("E" + (rowNum) + "*F" + (rowNum) + "*G" + (rowNum) + "*H" + (rowNum) + "/10000"); - amountCell.setCellStyle(formulaStyle); + Cell amountCell = dataRow.createCell(7); + amountCell.setCellFormula("D" + (rowNum) + "*E" + (rowNum) + "*F" + (rowNum) + "*G" + (rowNum) + "/10000"); + amountCell.setCellStyle(rdFormulaStyle); - createCell(dataRow, 9, detail.getProjectPersonnel(), dataStyle); + createCell(dataRow, 8, detail.getRemark(), rdRightDataStyle); return rowNum; } @@ -1649,19 +542,26 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { int rowNum = startRowNum; int fillRows = targetSize - dataSize; + int sortOrder = dataSize + 1; while (fillRows > 0) { - Row emptyRow = sheet.createRow(rowNum++); - createMergedCell(sheet, emptyRow, 2, 3, "", dataStyle); - for (int i = 4; i < 8; i++) { - Double value = null; - createNumericCell(emptyRow, i, value, dataStyle); + Row dataRow = sheet.getRow(rowNum); + if (dataRow == null) { + dataRow = sheet.createRow(rowNum++); } - Cell amountCell = emptyRow.createCell(8); - amountCell.setCellFormula("E" + (rowNum) + "*F" + (rowNum) + "*G" + (rowNum) + "*H" + (rowNum) + "/10000"); - amountCell.setCellStyle(formulaStyle); + createNumericCell(dataRow, 1, Double.valueOf(sortOrder++), rdLeftDataStyle); + createCell(dataRow, 2, "", rdDataStyle); - createCell(emptyRow, 9, "", dataStyle); + for (int i = 3; i <= 6; i++) { + Double value = null; + createNumericCell(dataRow, i, value, rdDataStyle); + } + + Cell amountCell = dataRow.createCell(7); + amountCell.setCellFormula("D" + (rowNum) + "*E" + (rowNum) + "*F" + (rowNum) + "*G" + (rowNum) + "/10000"); + amountCell.setCellStyle(rdFormulaStyle); + + createCell(dataRow, 8, "", rdRightDataStyle); fillRows--; } @@ -1671,257 +571,204 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { /** * 创建人工费合计行 */ - private int createLaborCostTotalRow(Sheet sheet, int dataStartRow, int rowNum) { - Row laborTotalRow = sheet.createRow(rowNum++); - - createMergedCell(sheet, laborTotalRow, 2, 7, "合计", headerStyle); - - Cell laborTotalCell = laborTotalRow.createCell(8); - laborTotalCell.setCellFormula(String.format("SUM(I%d:I%d)", dataStartRow, rowNum - 1)); - laborTotalCell.setCellStyle(formulaStyle); - - createCell(laborTotalRow, 9, "", dataStyle); - - return rowNum; - } - - /** - * 创建劳务费部分 - */ - private int createServiceCostSection(Sheet sheet, List budgetLaborCostList, int startRowNum) { + private int createLaborCostTotalRow(Sheet sheet, int dataStartRow, int startRowNum) { int rowNum = startRowNum; - // 劳务费部分标题 - Row serviceFeeTitleRow = sheet.createRow(rowNum++); - createMergedCell(sheet, serviceFeeTitleRow, 0, 8, "劳务费预算明细表", titleStyle); + Row laborTotalRow = sheet.createRow(rowNum); + laborTotalRow.setHeightInPoints(30); - int serviceFeeStartRowNum = rowNum; + createMergedCell(sheet, laborTotalRow, 1, 2, "合计", rdLeftFooterStyle); + createCell(laborTotalRow, 3, "", rdFooterStyle); + createCell(laborTotalRow, 4, "", rdFooterStyle); + createCell(laborTotalRow, 5, "", rdFooterStyle); + createCell(laborTotalRow, 6, "", rdFooterStyle); - // 劳务费表头 - Row serviceFeeHeaderRow = sheet.createRow(rowNum++); - createMergedCell(sheet, serviceFeeHeaderRow, 2, 3, "人员类别", headerStyle); - createCell(serviceFeeHeaderRow, 4, "人数", headerStyle); - createCell(serviceFeeHeaderRow, 5, "累计时间(月)", headerStyle); - createCell(serviceFeeHeaderRow, 6, "月平均投入比例(%)", headerStyle); - createCell(serviceFeeHeaderRow, 7, "人工标准(元/人月)", headerStyle); - createCell(serviceFeeHeaderRow, 8, "金额(万元)", headerStyle); + Cell laborTotalCell = laborTotalRow.createCell(7); + laborTotalCell.setCellFormula(String.format("SUM(H%d:H%d)", dataStartRow, rowNum)); + laborTotalCell.setCellStyle(rdFooterFormulaStyle); - // 过滤劳务费数据 - List serviceCostList = filterLaborCostByType(budgetLaborCostList, LaborTypeEnum.SERVICE_COST.getCode()); - - // 创建劳务费数据行 - int dataStartRow = rowNum + 1; - for (ErpRdBudgetLaborCost detail : serviceCostList) { - rowNum = createServiceCostDataRow(sheet, detail, rowNum); - } - - // 填充空行 - rowNum = fillServiceCostEmptyRows(sheet, serviceCostList.size(), rowNum, 3); - - // 劳务费合计 - rowNum = createServiceCostTotalRow(sheet, dataStartRow, rowNum); - - // 创建左侧合并单元格 - createDataRowsWithDynamicMerge(sheet, serviceFeeStartRowNum, rowNum - serviceFeeStartRowNum + 1, 0, 1, "劳务费"); + createCell(laborTotalRow, 8, "", rdRightFooterStyle); return rowNum; } - /** - * 创建劳务费数据行 - */ - private int createServiceCostDataRow(Sheet sheet, ErpRdBudgetLaborCost detail, int rowNum) { - Row dataRow = sheet.createRow(rowNum++); + private void createLaborInstruction(Sheet sheet, int rowNum) { + Row laborInstructionRow = sheet.createRow(rowNum + 2); + createCell(laborInstructionRow, 1, "注:", rdInstructionStyle); + Row laborInstructionRow2 = sheet.createRow(rowNum + 3); + laborInstructionRow2.setHeightInPoints(30); + createMergedCell(sheet, laborInstructionRow, 2, 7, "人工费的预算,人工标准,各项目组按照成员的大概平均工资水平进行估算。表格所给数据为建议参考数据。", rdInstructionStyle); + createMergedCell(sheet, laborInstructionRow2, 2, 7, "人员类别,按照各项目实际情况填写。如,产品不在本公司内加工和组装的,生产加工费已经核算在外协件里,不再有生产加工费。", rdInstructionStyle); - createMergedCell(sheet, dataRow, 2, 3, detail.getPersonnelCategory(), dataStyle); - createNumericCell(dataRow, 4, detail.getPeopleNumber() == null ? null : detail.getPeopleNumber().doubleValue(), dataStyle); - Double camulativeTime = detail.getCumulativeTime() != null ? detail.getCumulativeTime().doubleValue() : null; - createNumericCell(dataRow, 5, camulativeTime, dataStyle); - Double monthRate = detail.getMonthRate() != null ? detail.getMonthRate().divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).doubleValue() : null; - createNumericCell(dataRow, 6, monthRate, percentStyle); - Double artificialStandard = detail.getArtificialStandard() != null ? detail.getArtificialStandard().doubleValue() : null; - createNumericCell(dataRow, 7, artificialStandard, dataStyle); - - // 金额公式 - Cell amountCell = dataRow.createCell(8); - amountCell.setCellFormula("ROUND(E" + (rowNum) + "*F" + (rowNum) + "*G" + (rowNum) + "*H" + (rowNum) + "/10000,2)"); - amountCell.setCellStyle(formulaStyle); - - return rowNum; - } - - /** - * 填充劳务费空行 - */ - private int fillServiceCostEmptyRows(Sheet sheet, int dataSize, int startRowNum, int targetSize) { - int rowNum = startRowNum; - int fillRows = targetSize - dataSize; - - while (fillRows > 0) { - Row emptyRow = sheet.createRow(rowNum++); - createMergedCell(sheet, emptyRow, 2, 3, "", dataStyle); - for (int i = 4; i < 8; i++) { - Double value = null; - createNumericCell(emptyRow, i, value, dataStyle); - } - - Cell amountCell = emptyRow.createCell(8); - amountCell.setCellFormula("E" + (rowNum) + "*F" + (rowNum) + "*G" + (rowNum) + "*H" + (rowNum) + "/10000"); - amountCell.setCellStyle(formulaStyle); - - fillRows--; - } - - return rowNum; - } - - /** - * 创建劳务费合计行 - */ - private int createServiceCostTotalRow(Sheet sheet, int dataStartRow, int rowNum) { - Row serviceFeeTotalRow = sheet.createRow(rowNum); - - createMergedCell(sheet, serviceFeeTotalRow, 2, 7, "合计", headerStyle); - - Cell serviceFeeTotalCell = serviceFeeTotalRow.createCell(8); - serviceFeeTotalCell.setCellFormula(String.format("SUM(I%d:I%d)", dataStartRow, rowNum)); - serviceFeeTotalCell.setCellStyle(formulaStyle); - - return rowNum; } /** * 设置人工劳务咨询表列宽 */ private void setLaborServiceSheetColumnWidths(Sheet sheet) { - setColumnWidths(sheet, new int[]{12, 12, 15, 20, 15, 15, 20, 20, 20, 30}); + setColumnWidths(sheet, new int[]{5, 6, 15, 8, 15, 20, 20, 20, 20}); } - // =========== 资料/文献费明细表相关方法 =========== + + // =========== 差旅费、会议费、国际交流费明细表相关方法 =========== /** - * 创建资料/文献费明细表 + * 创建差旅费、交通费明细表 */ - private Integer createDocumentSheet(ErpBudgetInfoVo budget) { - Sheet sheet = workbook.createSheet(SHEET_DOCUMENT); - - // 准备数据 - List literatureCostList = null; - + private Integer createTravelSheet(ErpBudgetInfoVo budget) { + Sheet sheet = workbook.createSheet(RdBudgetItemEnums.TRAVEL.getSheetName()); int rowNum = 0; - // 创建表头 - rowNum = createDocumentSheetHeader(sheet, rowNum); + List erpRdBudgetTravelCostList = budget.getErpRdBudgetTravelCostList() == null + ? new ArrayList<>() : budget.getErpRdBudgetTravelCostList(); - int materialTotalDataRowNum = rowNum + 1; + // 创建差旅费部分 + rowNum = createTravelSection(sheet, erpRdBudgetTravelCostList, rowNum); - // 创建资料费部分 - rowNum = createMaterialCostSection(sheet, literatureCostList, rowNum); + addTotalRowMapping(RdBudgetItemEnums.TRAVEL.getChildSheetName(), rowNum); - // 创建文献检索费部分 - rowNum = createDocumentRetrievalSection(sheet, literatureCostList, rowNum); + rowNum++; + // 创建交通费部分 + rowNum = createTransportationSection(sheet, erpRdBudgetTravelCostList, rowNum); - int documentRetrievalDataRowNum = rowNum; - - int softwarePurchaseDataRowNum = documentRetrievalDataRowNum + 1; - - // 创建专用软件购买费部分 - rowNum = createSoftwarePurchaseSection(sheet, literatureCostList, rowNum); - - // 创建合计行 - rowNum = createDocumentTotalRow(sheet, rowNum, materialTotalDataRowNum, documentRetrievalDataRowNum, softwarePurchaseDataRowNum); + createTravelInstruction(sheet, rowNum); // 设置列宽 - setDocumentSheetColumnWidths(sheet); + setTravelColumnWidths(sheet); return rowNum; } /** - * 创建资料/文献费表头 + * 创建差旅费部分 */ - private int createDocumentSheetHeader(Sheet sheet, int startRowNum) { + private int createTravelSection(Sheet sheet, List erpRdBudgetTravelCostList, int startRowNum) { int rowNum = startRowNum; - // 表头 - Row row1 = sheet.createRow(rowNum++); - createMergedCell(sheet, row1, 0, 1, "表六", titleStyle); + //创建表头 + rowNum = createTravelHeaderRows(sheet, rowNum); - Row row2 = sheet.createRow(rowNum++); - createMergedCell(sheet, row2, 0, 1, "资料/文献费预算表", titleStyle); + List travelCostList = filterTravelCostByType(erpRdBudgetTravelCostList, TripTypeEnum.TRAVEL.getCode()); - Row headerRow = sheet.createRow(rowNum++); - createCell(headerRow, 0, "项目", dataStyle); - createCell(headerRow, 1, "金额(万元)", dataStyle); - - return rowNum; - } - - /** - * 创建资料费部分 - */ - private int createMaterialCostSection(Sheet sheet, List literatureCostList, int startRowNum) { - int rowNum = startRowNum; - - // 资料费标题 - Row materialTitleRow = sheet.createRow(rowNum++); - createCell(materialTitleRow, 0, "一、资料费", headerStyle); - - Cell materialCostCell = materialTitleRow.createCell(1); - materialCostCell.setCellStyle(formulaStyle); - - // 过滤资料费数据 - List materialCostList = filterLiteratureCostByType(literatureCostList, LiteratureTypeEnum.MATERIAL_COST.getCode()); - - // 创建资料费数据行 - int dataStartRow = rowNum + 1; - for (ErpRdBudgetLiteratureCost detail : materialCostList) { - rowNum = createMaterialCostDataRow(sheet, detail, rowNum); - } + // 数据行 + int travelDataStartRow = rowNum + 1; + rowNum = createTravelDataRows(sheet, travelCostList, rowNum); // 填充空行 - rowNum = fillDocumentEmptyRows(sheet, materialCostList.size(), rowNum, 5); + rowNum = fillTravelCostEmptyRows(sheet, travelCostList.size(), rowNum, 8); - // 设置合计公式 - materialCostCell.setCellFormula(String.format("SUM(B%d:B%d)", dataStartRow, rowNum)); + + // 差旅费合计 + rowNum = createTravelTotalRow(sheet, travelDataStartRow, rowNum); + + // 创建左侧合并单元格 + createDataRowsWithDynamicMerge(sheet, 1, rowNum, 1, 1, "差\n旅\n费"); + + + return rowNum; + } + + private int createTravelHeaderRows(Sheet sheet, int startRowNum) { + int rowNum = startRowNum; + // 表头 + Row row1 = sheet.createRow(rowNum++); + createMergedCell(sheet, row1, 1, 12, "差旅费预算明细表(表4-1)", rdTitleStyle); + + Row travelHeaderRow = sheet.createRow(rowNum++); + String[] travelHeaders = {"序号", "出差地点", "事由", "次数", "人数", "天数", "住宿标准(元)", "往返路费(元)", "住宿费(元)", "餐补(元)", "小计(万元)"}; + for (int i = 0; i < travelHeaders.length; i++) { + if (i == travelHeaders.length - 1) { + createCell(travelHeaderRow, i + 2, travelHeaders[i], rdRightHeaderStyle); + } else { + createCell(travelHeaderRow, i + 2, travelHeaders[i], rdHeaderStyle); + } + } + + return rowNum; + } + + + /** + * 创建差旅费数据行 + */ + private int createTravelDataRows(Sheet sheet, List travelCostList, int startRowNum) { + int rowNum = startRowNum; + + for (int i = 0; i < travelCostList.size(); i++) { + ErpRdBudgetTravelCost detail = travelCostList.get(i); + rowNum = createTravelDataRow(sheet, detail, rowNum); + } return rowNum; } /** - * 按类型筛选资料文献费数据 + * 创建单个差旅费数据行 */ - private List filterLiteratureCostByType(List list, String type) { - return list.stream() - .filter(item -> item.getLiteratureType().equals(type)) - .toList(); - } - - /** - * 创建资料费数据行 - */ - private int createMaterialCostDataRow(Sheet sheet, ErpRdBudgetLiteratureCost detail, int rowNum) { + private int createTravelDataRow(Sheet sheet, ErpRdBudgetTravelCost detail, int rowNum) { Row dataRow = sheet.createRow(rowNum++); - createCell(dataRow, 0, detail.getItemDesc(), headerStyle); - Double price = detail.getPrice() != null ? detail.getPrice().doubleValue() : null; - createNumericCell(dataRow, 1, price, dataStyle); + createCell(dataRow, 2, detail.getSortOrder().toString(), rdDataStyle); + createCell(dataRow, 3, detail.getTripLocation(), rdDataStyle); + createCell(dataRow, 4, detail.getReason(), rdDataStyle); + createNumericCell(dataRow, 5, detail.getFrequency() == null ? null : detail.getFrequency().doubleValue(), rdDataStyle); + createNumericCell(dataRow, 6, detail.getPeopleNumber() == null ? null : detail.getPeopleNumber().doubleValue(), rdDataStyle); + createNumericCell(dataRow, 7, detail.getDays(), rdDataStyle); + createNumericCell(dataRow, 8, detail.getStayStandard(), rdDataStyle); + createNumericCell(dataRow, 9, detail.getTravelExpenses(), rdDataStyle); + + // 公式计算 + createTravelFormulas(dataRow, rowNum); return rowNum; } /** - * 填充资料文献费空行 + * 创建差旅费公式 */ - private int fillDocumentEmptyRows(Sheet sheet, int dataSize, int startRowNum, int targetSize) { + private void createTravelFormulas(Row row, int rowNum) { + Cell stayCostsCell = row.createCell(10); + stayCostsCell.setCellFormula("F" + (rowNum) + "*G" + (rowNum) + "*H" + (rowNum) + "*I" + (rowNum)); + stayCostsCell.setCellStyle(rdFormulaStyle); + + Cell subsidyCostsCell = row.createCell(11); + subsidyCostsCell.setCellFormula("F" + (rowNum) + "*G" + (rowNum) + "*H" + (rowNum) + "*90"); + subsidyCostsCell.setCellStyle(rdFormulaStyle); + + Cell subtotalCostsCell = row.createCell(12); + subtotalCostsCell.setCellFormula(String.format("(J%d+K%d+L%d)/10000", rowNum, rowNum, rowNum)); + subtotalCostsCell.setCellStyle(rdRightFormulaStyle); + + } + + + /** + * 填充人工费空行 + */ + private int fillTravelCostEmptyRows(Sheet sheet, int dataSize, int startRowNum, int targetSize) { int rowNum = startRowNum; int fillRows = targetSize - dataSize; + int sortOrder = dataSize + 1; while (fillRows > 0) { - Row emptyRow = sheet.createRow(rowNum++); - for (int i = 0; i < 2; i++) { - createCell(emptyRow, i, "", dataStyle); + Row dataRow = sheet.getRow(rowNum); + if (dataRow == null) { + dataRow = sheet.createRow(rowNum++); } + + createNumericCell(dataRow, 2, Double.valueOf(sortOrder++), rdDataStyle); + + for (int i = 3; i <= 4; i++) { + createCell(dataRow, i, "", rdDataStyle); + } + + for (int i = 5; i <= 9; i++) { + Double value = null; + createNumericCell(dataRow, i, value, rdDataStyle); + } + + createTravelFormulas(dataRow, rowNum); + fillRows--; } @@ -1929,103 +776,197 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { } /** - * 创建文献检索费部分 + * 创建差旅费合计行 */ - private int createDocumentRetrievalSection(Sheet sheet, List literatureCostList, int startRowNum) { - int rowNum = startRowNum; + private int createTravelTotalRow(Sheet sheet, int dataStartRow, int rowNum) { + Row travelTotalRow = sheet.createRow(rowNum); + travelTotalRow.setHeightInPoints(30); - // 文献检索费 - Optional documentCostOptional = literatureCostList.stream() - .filter(item -> item.getLiteratureType().equals(LiteratureTypeEnum.SOFTWARE_COST.getCode())) - .findFirst(); - - ErpRdBudgetLiteratureCost documentCost = new ErpRdBudgetLiteratureCost(); - if (documentCostOptional.isPresent()) { - documentCost = documentCostOptional.get(); + for (int i = 2; i <= 8; i++) { + createCell(travelTotalRow, i, "", rdFooterStyle); } - Row documentRow = sheet.createRow(rowNum++); - createCell(documentRow, 0, "二、文献检索费", headerStyle); - Double price = documentCost.getPrice() != null ? documentCost.getPrice().doubleValue() : null; - createNumericCell(documentRow, 1, price, dataStyle); + Cell travelExpensesTotalCell = travelTotalRow.createCell(9); + travelExpensesTotalCell.setCellFormula(String.format("SUM(J%d:J%d)", dataStartRow, rowNum)); + travelExpensesTotalCell.setCellStyle(rdFooterFormulaStyle); + + Cell stayCostsTotalCell = travelTotalRow.createCell(10); + stayCostsTotalCell.setCellFormula(String.format("SUM(K%d:K%d)", dataStartRow, rowNum)); + stayCostsTotalCell.setCellStyle(rdFooterFormulaStyle); + + Cell subsidyCostsTotalCell = travelTotalRow.createCell(11); + subsidyCostsTotalCell.setCellFormula(String.format("SUM(L%d:L%d)", dataStartRow, rowNum)); + subsidyCostsTotalCell.setCellStyle(rdFooterFormulaStyle); + + Cell travelTotalCell = travelTotalRow.createCell(12); + travelTotalCell.setCellFormula(String.format("SUM(M%d:M%d)", dataStartRow, rowNum)); + travelTotalCell.setCellStyle(rdFooterRightFormulaStyle); return rowNum; } + /** - * 创建专用软件购买费部分 + * 创建差旅费部分 */ - private int createSoftwarePurchaseSection(Sheet sheet, List literatureCostList, int startRowNum) { + private int createTransportationSection(Sheet sheet, List erpRdBudgetTravelCostList, int startRowNum) { int rowNum = startRowNum; - // 专用软件购买费标题 - Row softwareTitleRow = sheet.createRow(rowNum++); - createCell(softwareTitleRow, 0, "三、专用软件购买费", headerStyle); + //创建表头 + rowNum = createTransportationHeaderRows(sheet, rowNum); - Cell softwareCostCell = softwareTitleRow.createCell(1); - softwareCostCell.setCellStyle(formulaStyle); + List travelCostList = filterTravelCostByType(erpRdBudgetTravelCostList, TripTypeEnum.TRANSPORTATION.getCode()); - // 过滤专用软件购买费数据 - List softwareCostList = filterLiteratureCostByType(literatureCostList, LiteratureTypeEnum.SOFTWARE_COST.getCode()); - - // 创建专用软件购买费数据行 - int dataStartRow = rowNum + 1; - for (ErpRdBudgetLiteratureCost detail : softwareCostList) { - rowNum = createSoftwarePurchaseDataRow(sheet, detail, rowNum); - } + // 数据行 + int transportationDataStartRow = rowNum + 1; + rowNum = createTransportationDataRows(sheet, travelCostList, rowNum); // 填充空行 - rowNum = fillDocumentEmptyRows(sheet, softwareCostList.size(), rowNum, 6); + rowNum = fillTransportationCostEmptyRows(sheet, travelCostList.size(), rowNum, 5); - // 设置合计公式 - softwareCostCell.setCellFormula(String.format("SUM(B%d:B%d)", dataStartRow, rowNum)); + // 交通费合计 + rowNum = createTransportationTotalRow(sheet, transportationDataStartRow, rowNum); + + // 创建左侧合并单元格 + createDataRowsWithDynamicMerge(sheet, transportationDataStartRow - 2, rowNum - transportationDataStartRow + 3, 1, 1, "交\n通\n费"); return rowNum; } - /** - * 创建专用软件购买费数据行 - */ - private int createSoftwarePurchaseDataRow(Sheet sheet, ErpRdBudgetLiteratureCost detail, int rowNum) { - Row dataRow = sheet.createRow(rowNum++); - - createCell(dataRow, 0, detail.getItemDesc(), headerStyle); - Double price = detail.getPrice() != null ? detail.getPrice().doubleValue() : null; - createNumericCell(dataRow, 1, price, dataStyle); - - return rowNum; - } - - /** - * 创建资料/文献费合计行 - */ - private int createDocumentTotalRow(Sheet sheet, int startRowNum, int materialTotalDataRowNum, int documentRetrievalDataRowNum, int softwarePurchaseDataRowNum) { + private int createTransportationHeaderRows(Sheet sheet, int startRowNum) { int rowNum = startRowNum; - Row totalRow = sheet.createRow(rowNum); - createCell(totalRow, 0, "合计", headerStyle); + Row transportationHeaderRow = sheet.createRow(rowNum++); + createCell(transportationHeaderRow, 2, "序号", rdHeaderStyle); + createMergedCell(sheet, transportationHeaderRow, 3, 5, "出行起止地点", rdHeaderStyle); + createMergedCell(sheet, transportationHeaderRow, 6, 10, "出行任务", rdHeaderStyle); + createCell(transportationHeaderRow, 11, "金额(元)", rdHeaderStyle); + createCell(transportationHeaderRow, 12, "小计(万元)", rdRightHeaderStyle); - Cell totalCell = totalRow.createCell(1); - totalCell.setCellFormula(String.format("SUM(B%d,B%d,B%d)", materialTotalDataRowNum, documentRetrievalDataRowNum, softwarePurchaseDataRowNum)); - totalCell.setCellStyle(formulaStyle); + return rowNum; + } + + + /** + * 创建差旅费数据行 + */ + private int createTransportationDataRows(Sheet sheet, List travelCostList, int startRowNum) { + int rowNum = startRowNum; + + for (int i = 0; i < travelCostList.size(); i++) { + ErpRdBudgetTravelCost detail = travelCostList.get(i); + rowNum = createTransportationDataRow(sheet, detail, rowNum); + } return rowNum; } /** - * 设置资料/文献费表列宽 + * 创建单个差旅费数据行 */ - private void setDocumentSheetColumnWidths(Sheet sheet) { - setColumnWidths(sheet, new int[]{30, 50}); + private int createTransportationDataRow(Sheet sheet, ErpRdBudgetTravelCost detail, int rowNum) { + Row dataRow = sheet.createRow(rowNum++); + + createCell(dataRow, 2, detail.getSortOrder().toString(), rdDataStyle); + createMergedCell(sheet, dataRow, 3, 5, detail.getTripLocation(), rdDataStyle); + createMergedCell(sheet, dataRow, 6, 10, detail.getReason(), rdDataStyle); + createNumericCell(dataRow, 11, detail.getSubtotalCosts(), rdDataStyle); + + createTransportationFormulas(dataRow, rowNum); + + return rowNum; } + /** + * 创建差旅费公式 + */ + private void createTransportationFormulas(Row row, int rowNum) { + Cell subtotalCostsCell = row.createCell(12); + subtotalCostsCell.setCellFormula(String.format("L%d/10000", rowNum)); + subtotalCostsCell.setCellStyle(rdRightFormulaStyle); + + } + + + /** + * 填充人工费空行 + */ + private int fillTransportationCostEmptyRows(Sheet sheet, int dataSize, int startRowNum, int targetSize) { + int rowNum = startRowNum; + int fillRows = targetSize - dataSize; + + int sortOrder = dataSize + 1; + while (fillRows > 0) { + Row dataRow = sheet.getRow(rowNum); + if (dataRow == null) { + dataRow = sheet.createRow(rowNum++); + } + + createNumericCell(dataRow, 2, Double.valueOf(sortOrder++), rdDataStyle); + createMergedCell(sheet, dataRow, 3, 5, "", rdDataStyle); + createMergedCell(sheet, dataRow, 6, 10, "", rdDataStyle); + Double subsidyCosts = null; + createNumericCell(dataRow, 11, subsidyCosts, rdDataStyle); + + createTransportationFormulas(dataRow, rowNum); + + fillRows--; + } + + return rowNum; + } + + /** + * 创建差旅费合计行 + */ + private int createTransportationTotalRow(Sheet sheet, int dataStartRow, int rowNum) { + Row transportationTotalRow = sheet.createRow(rowNum); + transportationTotalRow.setHeightInPoints(30); + + createMergedCell(sheet, transportationTotalRow, 2, 11, "合计", rdFooterStyle); + + Cell transportationTotalCell = transportationTotalRow.createCell(12); + transportationTotalCell.setCellFormula(String.format("SUM(M%d:M%d)", dataStartRow, rowNum)); + transportationTotalCell.setCellStyle(rdFooterRightFormulaStyle); + + return rowNum; + } + + + private void createTravelInstruction(Sheet sheet, int rowNum) { + int startRow = rowNum + 1; + int endRow = startRow; + String noteText = "注:开展实验(试验)、考察、业务调研、学术交流等发生的外埠差旅费、市内交通费用等。开支标准按照公司有关规定执行。"; + List keywords = Arrays.asList("注:"); + + createMergedCellWithBoldKeywords(sheet, startRow, endRow, 1, 12, + noteText, keywords, rdInstructionStyle, 20); + } + + /** + * 按类型筛选差旅费数据 + */ + private List filterTravelCostByType(List list, String type) { + return list.stream() + .filter(item -> item.getTripType().equals(type)) + .toList(); + } + + /** + * 设置差旅会议交流表列宽 + */ + private void setTravelColumnWidths(Sheet sheet) { + setColumnWidths(sheet, new int[]{6, 5, 7, 15, 13, 8, 8, 10, 15, 15, 15, 15, 15}); + } + + // =========== 测试化验费明细表相关方法 =========== /** * 创建测试化验费明细表 */ private Integer createTestingSheet(ErpBudgetInfoVo budget) { - Sheet sheet = workbook.createSheet(SHEET_TESTING); + Sheet sheet = workbook.createSheet(RdBudgetItemEnums.TESTING.getSheetName()); // 准备数据 List testingCostList = prepareTestingCostData(budget); @@ -2040,11 +981,12 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { rowNum = createTestingDataRows(sheet, testingCostList, rowNum); // 填充空行 - rowNum = fillTestingEmptyRows(sheet, testingCostList.size(), rowNum, 12); + rowNum = fillTestingEmptyRows(sheet, testingCostList.size(), rowNum, 5); // 创建合计行 rowNum = createTestingTotalRow(sheet, dataStartRow, rowNum); + createTestingInstruction(sheet, rowNum); // 设置列宽 setTestingSheetColumnWidths(sheet); @@ -2055,9 +997,8 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { * 准备测试化验费数据 */ private List prepareTestingCostData(ErpBudgetInfoVo budget) { - List testingCostList = budget.getErpRdBudgetTestingCostList() == null ? + return budget.getErpRdBudgetTestingCostList() == null ? new ArrayList<>() : budget.getErpRdBudgetTestingCostList(); - return testingCostList; } /** @@ -2066,21 +1007,24 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { private int createTestingSheetHeader(Sheet sheet, int startRowNum) { int rowNum = startRowNum; - // 表头 - Row row1 = sheet.createRow(rowNum++); - createMergedCell(sheet, row1, 0, 6, "表七", titleStyle); - - Row row2 = sheet.createRow(rowNum++); - createMergedCell(sheet, row2, 0, 6, "测试化验费预算明细表", titleStyle); + // title + Row titleRow = sheet.createRow(rowNum++); + createMergedCell(sheet, titleRow, 1, 7, "测试化验加工费预算明细表(表5-1)", rdTitleStyle); // 表头 Row headerRow = sheet.createRow(rowNum++); headerRow.setHeightInPoints(40); - String[] headers = {"序号", "测试化验的内容", "测试化验单位", "单位", "单价\n(元/单位数量)", "数量", "金额(万元)"}; - for (int i = 0; i < headers.length; i++) { - createCell(headerRow, i, headers[i], headerStyle); - } + String[] headers = {"序\n号", "测试化验加工的内容", "测试化验加工单位", "单位", "单价(元)", "数量", "金额(万元)"}; + for (int i = 0; i < headers.length; i++) { + if (i == 0) { + createCell(headerRow, i + 1, headers[i], rdLeftHeaderStyle); + } else if (i == headers.length - 1) { + createCell(headerRow, i + 1, headers[i], rdRightHeaderStyle); + } else { + createCell(headerRow, i + 1, headers[i], rdHeaderStyle); + } + } return rowNum; } @@ -2103,19 +1047,17 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { private int createTestingDataRow(Sheet sheet, ErpRdBudgetTestingCost detail, int rowNum) { Row dataRow = sheet.createRow(rowNum++); - createCell(dataRow, 0, detail.getSortOrder().toString(), dataStyle); - createCell(dataRow, 1, detail.getTestingContent(), dataStyle); - createCell(dataRow, 2, detail.getTestingUnitName(), dataStyle); - createCell(dataRow, 3, detail.getUnitName(), dataStyle); + createCell(dataRow, 1, detail.getSortOrder().toString(), rdLeftDataStyle); + createCell(dataRow, 2, detail.getTestingContent(), rdDataStyle); + createCell(dataRow, 3, detail.getTestingUnitName(), rdDataStyle); + createCell(dataRow, 4, detail.getUnitName(), rdDataStyle); Double unitPrice = detail.getUnitPrice() != null ? detail.getUnitPrice().doubleValue() : null; - createNumericCell(dataRow, 4, unitPrice, dataStyle); + createNumericCell(dataRow, 5, unitPrice, rdDataStyle); Double amount = detail.getAmount() != null ? detail.getAmount().doubleValue() : null; - createNumericCell(dataRow, 5, amount, dataStyle); + createNumericCell(dataRow, 6, amount, rdDataStyle); // 金额公式 - Cell amountCell = dataRow.createCell(6); - amountCell.setCellFormula("ROUND(E" + (rowNum) + "*F" + (rowNum) + "/10000,2)"); - amountCell.setCellStyle(formulaStyle); + createTestingFormula(dataRow, rowNum); return rowNum; } @@ -2127,19 +1069,228 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { int rowNum = startRowNum; int fillRows = targetSize - dataSize; + int sortOrder = dataSize + 1; while (fillRows > 0) { Row emptyRow = sheet.createRow(rowNum++); - for (int i = 0; i < 4; i++) { - createCell(emptyRow, i, "", dataStyle); + createNumericCell(emptyRow, 1, Double.valueOf(sortOrder++), rdLeftDataStyle); + + for (int i = 2; i <= 4; i++) { + createCell(emptyRow, i, "", rdDataStyle); } - for (int i = 4; i < 6; i++) { + for (int i = 5; i <= 6; i++) { Double value = null; - createNumericCell(emptyRow, i, value, dataStyle); + createNumericCell(emptyRow, i, value, rdDataStyle); } - Cell amountCell = emptyRow.createCell(6); - amountCell.setCellFormula("ROUND(E" + (rowNum) + "*F" + (rowNum) + "/10000,2)"); - amountCell.setCellStyle(formulaStyle); + createTestingFormula(emptyRow, rowNum); + + fillRows--; + } + + return rowNum; + } + + private void createTestingFormula(Row row, int rowNum) { + Cell amountCell = row.createCell(7); + amountCell.setCellFormula("ROUND(F" + (rowNum) + "*G" + (rowNum) + "/10000,2)"); + amountCell.setCellStyle(rdRightFormulaStyle); + } + + + /** + * 创建测试化验费合计行 + */ + private int createTestingTotalRow(Sheet sheet, int dataStartRow, int rowNum) { + Row totalRow = sheet.createRow(rowNum); + totalRow.setHeightInPoints(30); + + createMergedCell(sheet, totalRow, 1, 2, "合计", rdLeftFooterStyle); + createCell(totalRow, 3, "", rdFooterStyle); + createCell(totalRow, 4, "", rdFooterStyle); + createCell(totalRow, 5, "", rdFooterStyle); + createCell(totalRow, 6, "", rdFooterStyle); + + Cell totalCell = totalRow.createCell(7); + totalCell.setCellFormula(String.format("SUM(H%d:H%d)", dataStartRow, rowNum)); + totalCell.setCellStyle(rdFooterRightFormulaStyle); + + return rowNum; + } + + private void createTestingInstruction(Sheet sheet, int rowNum) { + int startRow = rowNum + 1; + int endRow = startRow; + String noteText = "注:是指支付给外单位(包括公司内部经济核算单位)的检验、测试、化验及加工等费用。"; + List keywords = Arrays.asList("注:"); + + createMergedCellWithBoldKeywords(sheet, startRow, endRow, 1, 7, + noteText, keywords, rdInstructionStyle, 20); + } + + /** + * 设置测试化验费表列宽 + */ + private void setTestingSheetColumnWidths(Sheet sheet) { + setColumnWidths(sheet, new int[]{8, 5, 30, 30, 15, 15, 10, 20}); + } + + + // =========== 咨询费.设计费明细表相关方法 =========== + + /** + * 创建咨询费.设计费明细表 + */ + private Integer createTechSheet(ErpBudgetInfoVo budget) { + Sheet sheet = workbook.createSheet(RdBudgetItemEnums.CONSULTATION.getSheetName()); + int rowNum = 0; + + List erpRdBudgetTechCostList = budget.getErpRdBudgetTechCostList() == null + ? new ArrayList<>() : budget.getErpRdBudgetTechCostList(); + + // 创建专家咨询费-会议形式部分 + rowNum = createExpertMeetingSection(sheet, erpRdBudgetTechCostList, rowNum); + + int expertMeetingRowNum = rowNum; + + // 创建专家咨询费-通讯形式部分 + rowNum = createExpertCommunicationSection(sheet, erpRdBudgetTechCostList, rowNum); + + int expertCommunicationRowNum = rowNum; + + addTotalRowMapping(RdBudgetItemEnums.CONSULTATION.getChildSheetName(), rowNum); + + rowNum = createExpertTotalRow(sheet, rowNum, expertMeetingRowNum, expertCommunicationRowNum); + + rowNum = createProductDesignSection(sheet, erpRdBudgetTechCostList, rowNum); + + // 设置列宽 + setTechSheetColumnWidths(sheet); + + return rowNum; + } + + /** + * 创建专家咨询费-会议形式部分 + */ + private int createExpertMeetingSection(Sheet sheet, List erpRdBudgetTechCostList, int startRowNum) { + int rowNum = startRowNum; + + //创建表头 + Row titleRow = sheet.createRow(rowNum++); + createMergedCell(sheet, titleRow, 1, 8, "专家咨询费预算明细表(表6-1)", rdTitleStyle); + + rowNum = createExpertHeaderRows(sheet, rowNum); + + List expertMeetingCostList = filterTechCostByType(erpRdBudgetTechCostList, TechTypeEnum.EXPERT_MEETING.getCode()); + + // 数据行 + int expertMeetingDataStartRow = rowNum + 1; + rowNum = createExpertDataRows(sheet, expertMeetingCostList, rowNum); + + // 填充空行 + rowNum = fillExpertCostEmptyRows(sheet, expertMeetingCostList.size(), rowNum, 3); + + // 差旅费合计 + rowNum = createExpertSubtotalRow(sheet, expertMeetingDataStartRow, rowNum); + + // 创建左侧合并单元格 + createDataRowsWithDynamicMerge(sheet, 1, rowNum - 1, 1, 1, "会议形式"); + + + return rowNum; + } + + /** + * 创建专家咨询费-通讯形式部分 + */ + private int createExpertCommunicationSection(Sheet sheet, List erpRdBudgetTechCostList, int startRowNum) { + int rowNum = startRowNum; + + rowNum = createExpertHeaderRows(sheet, rowNum); + + List expertCommunicationCostList = filterTechCostByType(erpRdBudgetTechCostList, TechTypeEnum.EXPERT_COMMUNICATION.getCode()); + + // 数据行 + int expertCommunicationDataStartRow = rowNum + 1; + rowNum = createExpertDataRows(sheet, expertCommunicationCostList, rowNum); + + // 填充空行 + rowNum = fillExpertCostEmptyRows(sheet, expertCommunicationCostList.size(), rowNum, 3); + + // 差旅费合计 + rowNum = createExpertSubtotalRow(sheet, expertCommunicationDataStartRow, rowNum); + + // 创建左侧合并单元格 + createDataRowsWithDynamicMerge(sheet, expertCommunicationDataStartRow - 2, rowNum - expertCommunicationDataStartRow + 2, 1, 1, "通讯形式"); + + + return rowNum; + } + + + private int createExpertHeaderRows(Sheet sheet, int startRowNum) { + int rowNum = startRowNum; + // 表头 + Row expertMeetingHeaderRow = sheet.createRow(rowNum++); + createMergedCell(sheet, expertMeetingHeaderRow, 2, 4, "内容", rdHeaderStyle); + createCell(expertMeetingHeaderRow, 5, "专业方向", rdHeaderStyle); + createCell(expertMeetingHeaderRow, 6, "专家人数", rdHeaderStyle); + createCell(expertMeetingHeaderRow, 7, "天数", rdHeaderStyle); + createCell(expertMeetingHeaderRow, 8, "金额(万元)", rdRightHeaderStyle); + + return rowNum; + } + + + /** + * 创建差旅费数据行 + */ + private int createExpertDataRows(Sheet sheet, List techCostList, int startRowNum) { + int rowNum = startRowNum; + + for (int i = 0; i < techCostList.size(); i++) { + ErpRdBudgetTechCost detail = techCostList.get(i); + rowNum = createExpertDataRow(sheet, detail, rowNum); + } + + return rowNum; + } + + /** + * 创建单个差旅费数据行 + */ + private int createExpertDataRow(Sheet sheet, ErpRdBudgetTechCost detail, int rowNum) { + Row dataRow = sheet.createRow(rowNum++); + + createMergedCell(sheet, dataRow, 2, 4, detail.getTechContent(), rdDataStyle); + createCell(dataRow, 5, detail.getProfessionalDirection(), rdDataStyle); + createNumericCell(dataRow, 6, detail.getPeopleNumber().doubleValue(), rdDataStyle); + createNumericCell(dataRow, 7, detail.getDays(), rdDataStyle); + createNumericCell(dataRow, 8, detail.getPrice(), rdRightDataStyle); + + return rowNum; + } + + + /** + * 填充专家咨询费空行 + */ + private int fillExpertCostEmptyRows(Sheet sheet, int dataSize, int startRowNum, int targetSize) { + int rowNum = startRowNum; + int fillRows = targetSize - dataSize; + + while (fillRows > 0) { + Row dataRow = sheet.getRow(rowNum); + if (dataRow == null) { + dataRow = sheet.createRow(rowNum++); + } + + createMergedCell(sheet, dataRow, 2, 4, "", rdDataStyle); + createCell(dataRow, 5, "", rdDataStyle); + Double value = null; + createNumericCell(dataRow, 6, value, rdDataStyle); + createNumericCell(dataRow, 7, value, rdDataStyle); + createNumericCell(dataRow, 8, value, rdRightDataStyle); fillRows--; } @@ -2148,38 +1299,181 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { } /** - * 创建测试化验费合计行 + * 创建专家咨询费小计行 */ - private int createTestingTotalRow(Sheet sheet, int dataStartRow, int rowNum) { - Row totalRow = sheet.createRow(rowNum); + private int createExpertSubtotalRow(Sheet sheet, int dataStartRow, int rowNum) { + Row expertMeetingTotalRow = sheet.createRow(rowNum++); - createMergedCell(sheet, totalRow, 0, 1, "合计", headerStyle); - createCell(totalRow, 2, "/", headerStyle); - createCell(totalRow, 3, "/", headerStyle); - createCell(totalRow, 4, "/", headerStyle); - createCell(totalRow, 5, "/", headerStyle); + createMergedCell(sheet, expertMeetingTotalRow, 2, 7, "小计", rdDataStyle); - Cell totalCell = totalRow.createCell(6); - totalCell.setCellFormula(String.format("SUM(G%d:G%d)", dataStartRow, rowNum)); - totalCell.setCellStyle(formulaStyle); + Cell travelTotalCell = expertMeetingTotalRow.createCell(8); + travelTotalCell.setCellFormula(String.format("SUM(I%d:I%d)", dataStartRow, rowNum - 1)); + travelTotalCell.setCellStyle(rdRightFormulaStyle); + + return rowNum; + } + + + /** + * 创建专家咨询费合计行 + */ + private int createExpertTotalRow(Sheet sheet, int rowNum, int expertMeetingRowNum, int expertCommunicationRowNum) { + Row expertTotalRow = sheet.createRow(rowNum++); + expertTotalRow.setHeightInPoints(30); + + createMergedCell(sheet, expertTotalRow, 1, 7, "合计", rdLeftFooterStyle); + + Cell travelTotalCell = expertTotalRow.createCell(8); + travelTotalCell.setCellFormula(String.format("I%d+I%d", expertMeetingRowNum, expertCommunicationRowNum)); + travelTotalCell.setCellStyle(rdFooterFormulaStyle); + + return rowNum; + } + + + /** + * 创建新产品设计费 + */ + private int createProductDesignSection(Sheet sheet, List erpRdBudgetTechCostList, int startRowNum) { + int rowNum = startRowNum; + + rowNum = createProductDesignHeaderRows(sheet, rowNum); + + List productDesignCostList = filterTechCostByType(erpRdBudgetTechCostList, TechTypeEnum.TECH_CONSULT.getCode()); + + // 数据行 + int productDesignDataStartRow = rowNum + 1; + rowNum = createProductDesignDataRows(sheet, productDesignCostList, rowNum); + + // 填充空行 + rowNum = fillProductDesignCostEmptyRows(sheet, productDesignCostList.size(), rowNum, 4); + + addTotalRowMapping(RdBudgetItemEnums.DESIGN.getChildSheetName(), rowNum); + // 新产品设计费合计 + rowNum = createProductDesignTotalRow(sheet, productDesignDataStartRow, rowNum); + + return rowNum; + } + + + private int createProductDesignHeaderRows(Sheet sheet, int startRowNum) { + int rowNum = startRowNum + 2; + //创建表头 + Row titleRow = sheet.createRow(rowNum++); + createMergedCell(sheet, titleRow, 1, 8, "新产品设计费(表6-2)", rdTitleStyle); + + Row expertProductDesignHeaderRow = sheet.createRow(rowNum++); + createCell(expertProductDesignHeaderRow, 1, "序号", rdLeftHeaderStyle); + createMergedCell(sheet, expertProductDesignHeaderRow, 2, 4, "内容", rdHeaderStyle); + createMergedCell(sheet, expertProductDesignHeaderRow, 5, 6, "单位", rdHeaderStyle); + createCell(expertProductDesignHeaderRow, 7, "金额(万元)", rdHeaderStyle); + createCell(expertProductDesignHeaderRow, 8, "备注", rdRightHeaderStyle); + + return rowNum; + } + + + /** + * 创建差旅费数据行 + */ + private int createProductDesignDataRows(Sheet sheet, List techCostList, int startRowNum) { + int rowNum = startRowNum; + + for (int i = 0; i < techCostList.size(); i++) { + ErpRdBudgetTechCost detail = techCostList.get(i); + rowNum = createProductDesignDataRow(sheet, detail, rowNum); + } return rowNum; } + /** + * 创建单个差旅费数据行 + */ + private int createProductDesignDataRow(Sheet sheet, ErpRdBudgetTechCost detail, int rowNum) { + Row dataRow = sheet.createRow(rowNum++); + + createCell(dataRow, 1, detail.getSortOrder().toString(), rdDataStyle); + createMergedCell(sheet, dataRow, 2, 4, detail.getTechContent(), rdDataStyle); + createMergedCell(sheet, dataRow, 5, 6, detail.getUnitName(), rdDataStyle); + createNumericCell(dataRow, 7, detail.getPrice(), rdDataStyle); + createCell(dataRow, 8, detail.getRemark(), rdRightDataStyle); + + return rowNum; + } + + + /** + * 填充新产品设计费空行 + */ + private int fillProductDesignCostEmptyRows(Sheet sheet, int dataSize, int startRowNum, int targetSize) { + int rowNum = startRowNum; + int fillRows = targetSize - dataSize; + + int sortOrder = dataSize + 1; + while (fillRows > 0) { + Row dataRow = sheet.getRow(rowNum); + if (dataRow == null) { + dataRow = sheet.createRow(rowNum++); + } + + createNumericCell(dataRow, 1, Double.valueOf(sortOrder++), rdLeftDataStyle); + + createMergedCell(sheet, dataRow, 2, 4, "", rdDataStyle); + createMergedCell(sheet, dataRow, 5, 6, "", rdDataStyle); + Double value = null; + createNumericCell(dataRow, 7, value, rdDataStyle); + createCell(dataRow, 8, "", rdRightDataStyle); + + fillRows--; + } + + return rowNum; + } + + /** + * 创建差旅费合计行 + */ + private int createProductDesignTotalRow(Sheet sheet, int dataStartRow, int rowNum) { + Row productDesignTotalRow = sheet.createRow(rowNum++); + productDesignTotalRow.setHeightInPoints(30); + + createMergedCell(sheet, productDesignTotalRow, 1, 6, "合计", rdLeftFooterStyle); + + Cell productDesignTotalCell = productDesignTotalRow.createCell(7); + productDesignTotalCell.setCellFormula(String.format("SUM(H%d:H%d)", dataStartRow, rowNum - 1)); + productDesignTotalCell.setCellStyle(rdFooterFormulaStyle); + + createCell(productDesignTotalRow, 8, "", rdRightFooterStyle); + + return rowNum; + } + + /** + * 按类型筛选技术服务费数据 + */ + private List filterTechCostByType(List list, String type) { + return list.stream() + .filter(item -> item.getTechType().equals(type)) + .toList(); + } + + /** * 设置测试化验费表列宽 */ - private void setTestingSheetColumnWidths(Sheet sheet) { - setColumnWidths(sheet, new int[]{15, 30, 20, 15, 20, 20, 20}); + private void setTechSheetColumnWidths(Sheet sheet) { + setColumnWidths(sheet, new int[]{10, 10, 15, 15, 15, 15, 15, 15, 15}); } + // =========== 其他费用明细表相关方法 =========== /** * 创建其他费用明细表 */ private Integer createOtherSheet(ErpBudgetInfoVo budget) { - Sheet sheet = workbook.createSheet(SHEET_OTHER); + Sheet sheet = workbook.createSheet(RdBudgetItemEnums.OTHER.getSheetName()); // 准备数据 List otherCostList = prepareOtherCostData(budget); @@ -2194,7 +1488,7 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { rowNum = createOtherDataRows(sheet, otherCostList, rowNum); // 填充空行 - rowNum = fillOtherEmptyRows(sheet, otherCostList.size(), rowNum, 10); + rowNum = fillOtherEmptyRows(sheet, otherCostList.size(), rowNum, 8); // 创建合计行 rowNum = createOtherTotalRow(sheet, dataStartRow, rowNum); @@ -2206,44 +1500,50 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { } /** - * 准备其他费用数据 + * 准备测试化验费数据 */ private List prepareOtherCostData(ErpBudgetInfoVo budget) { - List otherCostList = budget.getErpRdBudgetOtherCostList() == null ? + return budget.getErpRdBudgetOtherCostList() == null ? new ArrayList<>() : budget.getErpRdBudgetOtherCostList(); - return otherCostList; } /** - * 创建其他费用表头 + * 创建测试化验费表头 */ private int createOtherSheetHeader(Sheet sheet, int startRowNum) { int rowNum = startRowNum; - // 表头 - Row row1 = sheet.createRow(rowNum++); - createMergedCell(sheet, row1, 0, 2, "表八", titleStyle); - - Row row2 = sheet.createRow(rowNum++); - createMergedCell(sheet, row2, 0, 2, "其他费用预算表", titleStyle); + // title + Row titleRow = sheet.createRow(rowNum++); + createMergedCell(sheet, titleRow, 1, 4, "其他费用预算表(表7)", rdTitleStyle); // 表头 Row headerRow = sheet.createRow(rowNum++); - String[] headers = {"序号", "项目", "金额(万元)"}; + headerRow.setHeightInPoints(20); + + String[] headers = {"序号", "费用项目", "金额(万元)", "费用说明"}; for (int i = 0; i < headers.length; i++) { - createCell(headerRow, i, headers[i], headerStyle); + if (i == 0) { + createCell(headerRow, i + 1, headers[i], rdLeftHeaderStyle); + } else if (i == headers.length - 1) { + createCell(headerRow, i + 1, headers[i], rdRightHeaderStyle); + } else { + createCell(headerRow, i + 1, headers[i], rdHeaderStyle); + } } + createMergedCell(sheet, headerRow, 5, 9, "费用说明仅供参考,请于提交预算时删除E列内容", rdDataStyle); + return rowNum; } /** - * 创建其他费用数据行 + * 创建其他费数据行 */ - private int createOtherDataRows(Sheet sheet, List otherCostList, int startRowNum) { + private int createOtherDataRows(Sheet sheet, List otherCostsCostList, int startRowNum) { int rowNum = startRowNum; - for (ErpRdBudgetOtherCost detail : otherCostList) { + for (ErpRdBudgetOtherCost detail : otherCostsCostList) { rowNum = createOtherDataRow(sheet, detail, rowNum); } @@ -2251,56 +1551,228 @@ public class RdProjectBudgetExcelExporter extends BaseExcelExporter { } /** - * 创建单个其他费用数据行 + * 创建单个其他费数据行 */ private int createOtherDataRow(Sheet sheet, ErpRdBudgetOtherCost detail, int rowNum) { Row dataRow = sheet.createRow(rowNum++); - createCell(dataRow, 0, detail.getSortOrder().toString(), dataStyle); - createCell(dataRow, 1, detail.getItemDesc(), dataStyle); - Double price = detail.getPrice() != null ? detail.getPrice().doubleValue() : null; - createNumericCell(dataRow, 2, price, moneyStyle); + createCell(dataRow, 1, detail.getSortOrder().toString(), rdLeftDataStyle); + createCell(dataRow, 2, detail.getItemDesc(), rdDataStyle); + createNumericCell(dataRow, 3, detail.getPrice(), rdDataStyle); + createCell(dataRow, 4, detail.getRemark(), rdRightDataStyle); return rowNum; } /** - * 填充其他费用空行 + * 填充测试化验费空行 */ private int fillOtherEmptyRows(Sheet sheet, int dataSize, int startRowNum, int targetSize) { int rowNum = startRowNum; int fillRows = targetSize - dataSize; + int sortOrder = dataSize + 1; while (fillRows > 0) { Row emptyRow = sheet.createRow(rowNum++); - for (int i = 0; i < 3; i++) { - createCell(emptyRow, i, "", dataStyle); - } + createNumericCell(emptyRow, 1, Double.valueOf(sortOrder++), rdLeftDataStyle); + + createCell(emptyRow, 2, "", rdDataStyle); + createNumericCell(emptyRow, 3, (Double) null, rdDataStyle); + createCell(emptyRow, 4, "", rdRightDataStyle); + fillRows--; } return rowNum; } + /** - * 创建其他费用合计行 + * 创建其他费合计行 */ private int createOtherTotalRow(Sheet sheet, int dataStartRow, int rowNum) { Row totalRow = sheet.createRow(rowNum); + totalRow.setHeightInPoints(30); - createMergedCell(sheet, totalRow, 0, 1, "合计", headerStyle); + createMergedCell(sheet, totalRow, 1, 2, "合计", rdLeftFooterStyle); - Cell totalCell = totalRow.createCell(2); - totalCell.setCellFormula(String.format("SUM(C%d:C%d)", dataStartRow, rowNum)); - totalCell.setCellStyle(formulaStyle); + Cell totalCell = totalRow.createCell(3); + totalCell.setCellFormula(String.format("SUM(D%d:D%d)", dataStartRow, rowNum)); + totalCell.setCellStyle(rdFooterFormulaStyle); + + createCell(totalRow, 4, "", rdRightFooterStyle); + + return rowNum; + } + + + /** + * 设置其他费表列宽 + */ + private void setOtherSheetColumnWidths(Sheet sheet) { + setColumnWidths(sheet, new int[]{6, 8, 25, 20, 30}); + } + + + // =========== 预算总表相关方法 =========== + + /** + * 创建预算总表 + */ + private void createBudgetSheet(Sheet sheet, ErpBudgetInfoVo budget) { + int rowNum = 0; + + // 创建表头 + rowNum = createBudgetSheetHeader(sheet, budget, rowNum); + + // 创建预算科目数据 + rowNum = createBudgetSubjects(sheet, rowNum); + + // 创建合计行 + rowNum = createBudgetTotalRow(sheet, rowNum); + + createBudgetInstruction(sheet, rowNum); + + // 设置列宽 + setBudgetSheetColumnWidths(sheet); + } + + /** + * 创建预算表头 + */ + private int createBudgetSheetHeader(Sheet sheet, ErpBudgetInfoVo budget, int startRowNum) { + int rowNum = startRowNum; + + // 表头 + Row titleRow = sheet.createRow(rowNum++); + createMergedCell(sheet, titleRow, 1, 5, "科研项目经费预算明细表(表1)", rdTitleStyle); + + + // 项目信息 + Row projectRow1 = sheet.createRow(rowNum++); + createCell(projectRow1, 1, "项目名称:", rdFormStyle); + setupProjectFormula(projectRow1, 3, "F7"); + + createCell(projectRow1, 5, "项目经理:" + (budget.getManagerName() == null ? "" : budget.getManagerName()), rdFormStyle); + + Row projectRow2 = sheet.createRow(rowNum++); + createCell(projectRow2, 1, "项目编号:", rdFormStyle); + setupProjectFormula(projectRow2, 3, "F8"); + + createCell(projectRow2, 4, "项目预算期间:" + (budget.getDuringOperation() == null ? "" : budget.getDuringOperation()), rdFormStyle); + createCell(projectRow2, 5, "金额单位:万元", rdFormStyle); return rowNum; } /** - * 设置其他费用表列宽 + * 创建预算科目 */ - private void setOtherSheetColumnWidths(Sheet sheet) { - setColumnWidths(sheet, new int[]{15, 25, 30}); + private int createBudgetSubjects(Sheet sheet, int startRowNum) { + int rowNum = startRowNum; + + // 预算科目表头 + Row headerRow = sheet.createRow(rowNum++); + String[] headers = {"序号", "预算科目名称", "项目经费", "项目经费占比", "数据来源"}; + for (int i = 0; i < headers.length; i++) { + if (i == 0) { + createCell(headerRow, i + 1, headers[i], rdLeftHeaderStyle); + } else if (i == headers.length - 1) { + createCell(headerRow, i + 1, headers[i], rdRightHeaderStyle); + } else { + createCell(headerRow, i + 1, headers[i], rdHeaderStyle); + } + } + + // 预算科目数据 + RdBudgetItemEnums[] rdBudgetItemEnums = RdBudgetItemEnums.values(); + + for (int i = 0; i < rdBudgetItemEnums.length; i++) { + Row dataRow = sheet.createRow(rowNum++); + createCell(dataRow, 1, String.valueOf(i + 1), rdLeftDataStyle); + createCell(dataRow, 2, rdBudgetItemEnums[i].getName(), rdDataStyle); + + setupBudgetCostFormula(dataRow, rdBudgetItemEnums[i]); + + createCell(dataRow, 5, "", rdRightDataStyle); + } + + return rowNum; } + + /** + * 设置项目经费公式 + */ + private void setupBudgetCostFormula(Row row, RdBudgetItemEnums rdBudgetItemEnums) { + Integer totalRow = getTotalRow(rdBudgetItemEnums.getChildSheetName()); + if (totalRow != null) { + Cell cell = row.createCell(3); + cell.setCellFormula(String.format("%s!%s%d", "'" + rdBudgetItemEnums.getSheetName() + "'", rdBudgetItemEnums.getSheetPosition(), totalRow + 1)); + cell.setCellStyle(rdFormulaStyle); + } else { + Double value = null; + createNumericCell(row, 3, value, rdDataStyle); + } + Cell cell = row.createCell(4); + cell.setCellFormula(String.format("D%d/$D$12", row.getRowNum() + 1)); + cell.setCellStyle(rdFormulaPercentageStyle); + } + + + /** + * 创建预算合计行 + */ + private int createBudgetTotalRow(Sheet sheet, int startRowNum) { + int rowNum = startRowNum; + + Row totalRow = sheet.createRow(rowNum++); + totalRow.setHeightInPoints(30); + createMergedCell(sheet, totalRow, 1, 2, "合计", rdLeftFooterStyle); + + Cell totalCell = totalRow.createCell(3); + totalCell.setCellFormula("SUM(D5:D11)"); + totalCell.setCellStyle(rdFooterFormulaStyle); + + Cell proportionCell = totalRow.createCell(4); + proportionCell.setCellFormula("SUM(E5:E11)"); + proportionCell.setCellStyle(rdFooterFormulaPercentageStyle); + + createCell(totalRow, 5, "", rdRightFooterStyle); + + return rowNum; + } + + /** + * 创建预算汇总说明 + */ + private void createBudgetInstruction(Sheet sheet, int rowNum) { + int startRow = rowNum + 1; + int endRow = startRow; + String noteText = "注: ① 第1-11行由表二到表七的数据自动链接生成,不需手动填写。\n" + + " ② 此表中所有茶色部分数据无需填写,由公式自动生成。"; + List keywords = Arrays.asList("注:"); + + createMergedCellWithBoldKeywords(sheet, startRow, endRow, 1, 3, + noteText, keywords, rdInstructionStyle, 30); + } + + /** + * 设置预算表列宽 + */ + private void setBudgetSheetColumnWidths(Sheet sheet) { + setColumnWidths(sheet, new int[]{5, 10, 20, 40, 30, 20}); + } + + // =========== 公式设置方法 =========== + + /** + * 设置项目信息 + */ + private void setupProjectFormula(Row row, int columnNum, String coverPosition) { + Cell cell = row.createCell(columnNum); + cell.setCellFormula(String.format("%s!%s", RD_SHEET_COVER, coverPosition)); + cell.setCellStyle(rdFormFormulaStyle); + } + + }