首页 > 其他分享 >Spring Boot + Apache POI 实现 Excel 导出:BOM物料清单生成器(支持中文文件名、样式美化、数据合并)

Spring Boot + Apache POI 实现 Excel 导出:BOM物料清单生成器(支持中文文件名、样式美化、数据合并)

时间:2025-01-20 16:58:26浏览次数:3  
标签:生成器 String Spring 单元格 Excel style private 设置 public

目录

引言

Apache POI操作Excel的实用技巧

1.合并单元格操作

2.设置单元格样式

1. 创建样式对象

2. 设置边框

3. 设置底色

4. 设置对齐方式

5. 设置字体样式

6.设置自动换行

7. 应用样式到单元格

3. 定位和操作指定单元格

4.实现标签-值的形式

5.列宽设置

1. 设置单个列宽

2. 批量设置多列宽度

6.数据格式化

1. 设置数字格式

2. 设置日期格式

代码展示

1.POM依赖

2.实体类Mode

3.Controller层

4.Service层

获取源码



引言

在最近的MES系统开发中,我们需要导出BOM物料清单,并且客户对样式有较高要求。这就涉及到对POI库样式的精细调整,包括设置表格边框合并单元格设置单元格底色等常见操作。我通过实现一种模板,使得样式设计既美观又实用,并可以根据这个模板创建其他自定义格式。这一模板的主要功能包括:设置Excel表格的边框样式、添加背景色、合并单元格以及采用标签-值的展示形式(如“订单编号:BH000001”)。接下来,我将分享如何通过这种模板实现灵活的Excel导出功能,满足不同业务需求。

如下图是POM清单的一个实现模版:

下面让我们来先学习怎么通过POI库来实现表格边框,合并单元格,设置单元格底色,以及采用标签-值的展示形式这一系列操作!

Apache POI操作Excel的实用技巧

1.合并单元格操作

这个代码片段创建了一个Excel文件,生成了一个名为 "BOM物料清单" 的工作表,在第2行合并了从第1列到第10列的单元格(从第A列到第J列)最后,代码将生成的Excel文件转换为字节数组并返回。

// 使用 try-with-resources 语句,确保 Workbook 在使用完毕后会自动关闭
try (Workbook workbook = new XSSFWorkbook()) {
    // 创建一个名为 "BOM物料清单" 的工作表
    Sheet sheet = workbook.createSheet("BOM物料清单");

    // 创建第2行,行号从0开始,因此创建第2行的行号是1
    Row titleRow = sheet.createRow(1);

    // 合并第2行的0列到9列的单元格,CellRangeAddress的参数:起始行、结束行、起始列、结束列
    sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 9));

    // 在合并后的区域中设置单元格内容
    Cell titleCell = titleRow.createCell(0); // 在第1列创建单元格
    titleCell.setCellValue("BOM物料清单"); // 设置显示内容,例如 "BOM物料清单"

    // 创建输出流,用于将工作簿内容写入输出流
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    
    // 将工作簿写入到输出流中
    workbook.write(outputStream);
    
    // 返回字节数组,outputStream.toByteArray() 返回Excel文件的二进制数据
    return outputStream.toByteArray();
}

效果如下:

2.设置单元格样式

// 1. 创建样式对象
CellStyle style = workbook.createCellStyle();

// 2. 设置边框
style.setBorderTop(BorderStyle.THIN);    // 上边框
style.setBorderBottom(BorderStyle.THIN); // 下边框
style.setBorderLeft(BorderStyle.THIN);   // 左边框
style.setBorderRight(BorderStyle.THIN);  // 右边框

// 3. 设置底色
style.setFillForegroundColor(IndexedColors.LIGHT_GREEN.getIndex()); // 设置颜色
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);           // 设置填充模式

// 4. 设置对齐方式
style.setAlignment(HorizontalAlignment.CENTER);      // 水平居中
style.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中

// 5. 设置字体样式
Font font = workbook.createFont();
font.setBold(true); // 设置加粗
font.setColor(IndexedColors.RED.getIndex()); // 设置字体颜色为红色
font.setFontHeightInPoints((short) 12); // 设置字体大小
style.setFont(font); // 将字体应用到样式中

// 6. 设置自动换行
style.setWrapText(true); // 启用自动换行

// 7. 应用样式到单元格
cell.setCellStyle(style);

代码效果:

  • 边框:单元格会有细线的上下左右边框。
  • 底色:单元格背景为浅绿色。
  • 对齐方式:单元格内容会居中对齐(水平和垂直)。
  • 字体样式:字体加粗,颜色为红色,字体大小为12磅。
  • 自动换行:如果单元格内容过长,内容会自动换行。

如下是对每一个小功能的详细解释:

1. 创建样式对象

createCellStyle(): 这个方法是创建一个新的 CellStyle 对象,所有的样式设置都会在这个对象上进行。CellStyle 可以定义单元格的外观,如边框、对齐方式、字体、填充颜色等。

2. 设置边框

setBorderTop:设置单元格的上边框样式为 THIN(细线)。BorderStyle 枚举提供了几种边框样式:THIN(细线)、THICK(粗线)、DOTTED(点线)、DASHED(虚线)等。

setBorderBottom:设置单元格的下边框样式。

setBorderLeft:设置单元格的左边框样式。

setBorderRight:设置单元格的右边框样式。

3. 设置底色

setFillForegroundColor:设置单元格的前景色(填充颜色)。在这里,IndexedColors.LIGHT_GREEN.getIndex() 获取了 IndexedColors 枚举中的 LIGHT_GREEN(浅绿色)的颜色索引值,getIndex() 方法返回该颜色的数字标识。IndexedColors 枚举包括常见颜色,如 YELLOWREDBLUE 等。

setFillPattern(FillPatternType.SOLID_FOREGROUND):设置单元格的填充模式为 SOLID_FOREGROUND,表示填充整个单元格背景色。FillPatternType 还可以选择其他模式,如 NO_FILL(不填充)SOLID_FOREGROUND(完全填充)等。

颜色解释:

LIGHT_GREENIndexedColors 中的一个颜色常量,代表一种浅绿色。可以将单元格的背景色设置为浅绿色,增加表格的视觉效果,使其更具可读性和美观。

4. 设置对齐方式

setAlignment(HorizontalAlignment.CENTER):设置单元格内容在水平方向上的对齐方式。HorizontalAlignment.CENTER 表示水平居中对齐。其他选项包括 LEFT(左对齐)和 RIGHT(右对齐)。

setVerticalAlignment(VerticalAlignment.CENTER):设置单元格内容在垂直方向上的对齐方式。VerticalAlignment.CENTER 表示垂直居中对齐。其他选项包括 TOP(顶部对齐)和 BOTTOM(底部对齐)。

5. 设置字体样式

createFont():通过 workbook.createFont() 创建一个新的 Font 对象,用于设置字体样式。

setBold(true):设置字体加粗。

setColor(IndexedColors.RED.getIndex()):设置字体颜色为红色。IndexedColors.RED.getIndex() 获取了 IndexedColorsRED(红色)的颜色索引。除了 REDIndexedColors 还包含多种颜色,如 BLACKBLUEGREEN 等。

setFontHeightInPoints((short) 12):设置字体的大小为12磅(points)。你可以根据需求调整字体大小。

setFont(font):将字体样式应用到 CellStyle 中,使得字体的加粗、颜色和大小在单元格中生效。

6.设置自动换行

setWrapText(true):启用自动换行功能。当单元格内容过长时,文本会自动换行,以避免内容超出单元格边界。此设置特别有用,尤其是在表格中包含多行文字时。

7. 应用样式到单元格

setCellStyle(style):将之前定义的 style 应用到目标单元格 cell 上。所有在 style 中设置的样式(如边框、底色、字体、对齐方式等)都会在该单元格中生效。

3. 定位和操作指定单元格

通过行列号定位

行号和列号的下标都是从0开始,比如第一行的下标是0,第一列的下标是0

Row row = sheet.createRow(3);  // 注意:行号从0开始,3代表第四行
Cell cell = row.createCell(0); // 列号也从0开始,0代表第一列
cell.setCellValue("单元格内容");
  • sheet.createRow(3):这将创建或返回Excel表格中的第4行(因为行号从0开始,3表示第4行)。
  • row.createCell(0):这将创建或返回第4行的第1列单元格(列号从0开始,0表示第一列)。
  • cell.setCellValue("单元格内容"):为该单元格设置值为 "单元格内容"。

4.实现标签-值的形式

下面这段代码实现了在 Excel 表格中创建标签-值的形式,即每行包含一个标签单元格和一个值单元格。通常这种格式用于展示诸如 "订单编号"、"产品编号" 等信息,并且标签和对应的值是相邻的单元格。以下是代码的详细解释:

private void createLabelValuePair(Row row, int startCol, 
                                  String label, String value, 
                                  CellStyle labelStyle, CellStyle valueStyle) {
    // 创建标签单元格
    Cell labelCell = row.createCell(startCol);
    labelCell.setCellValue(label);
    labelCell.setCellStyle(labelStyle);  // 标签使用一种样式(如带底色)

    // 创建值单元格
    Cell valueCell = row.createCell(startCol + 1);
    valueCell.setCellValue(value);
    valueCell.setCellStyle(valueStyle);  // 值使用另一种样式(如不带底色)
}

参数说明:

  • Row row:表示当前行对象。在该行上创建标签和值的单元格。
  • int startCol:表示开始列的列号。标签单元格的列号是从 startCol 开始,值单元格紧随其后,列号为 startCol + 1
  • String label:标签文本,例如 "订单编号"、"产品编号" 等。
  • String value:标签对应的值,例如 "BH00000002"、"CP00000002" 等。
  • CellStyle labelStyle:标签单元格的样式,可以设置字体、对齐方式、背景色等。
  • CellStyle valueStyle:值单元格的样式,用于设置不同于标签的样式。

功能:

  • 该方法用于创建一对标签和值,两个单元格位于同一行(row)。标签单元格位于 startCol 列,值单元格位于 startCol + 1 列。
  • 这两个单元格分别应用不同的样式(labelStylevalueStyle),从而使标签和值的显示效果有所区别。标签单元格可能使用不同的字体、颜色、背景等样式,而值单元格则可能有不同的格式。

使用示例:

Row row = sheet.createRow(3); // 注意:行号从0开始,3代表第四行
createLabelValuePair(row, 0, "订单编号", "BH00000002", headerStyle, valueStyle);
createLabelValuePair(row, 2, "产品编号", "CP00000002", headerStyle, valueStyle);

5.列宽设置

1. 设置单个列宽

代码通过 sheet.setColumnWidth() 方法来调整列宽。下面是详细的解释:

sheet.setColumnWidth(columnIndex, 15 * 256); // 15个字符宽度

解释:

  • sheet.setColumnWidth(columnIndex, 15 * 256):该方法设置 Excel 中指定列的宽度。
    • columnIndex:表示列的索引,Excel 中列的索引从 0 开始。例如,0 表示 A 列,1 表示 B 列,以此类推。
    • 15 * 256:列宽的单位是 "字符宽度",但在 POI 中,单位是字符宽度的 1/256,因此需要乘以 256
      • 15 表示列宽为 15 个字符宽度(即单元格中可以容纳15个字符长度)。
      • 256 是 POI 中列宽的缩放因子,表示字符宽度单位是 1/256 的一个字符宽度。所以 15 * 256 表示列宽为 15 个字符的宽度。

在这个例子中,sheet.setColumnWidth(columnIndex, 15 * 256) 将设置列的宽度为15个字符的宽度,columnIndex 列号是动态指定的。

2. 批量设置多列宽度

private void setColumnWidths(Sheet sheet) {
    int[] widths = {15, 15, 15, 12, 12, 12, 15, 12, 15, 12};
    for (int i = 0; i < widths.length; i++) {
        sheet.setColumnWidth(i, widths[i] * 256);
    }
}

解释:

  • int[] widths = {15, 15, 15, 12, 12, 12, 15, 12, 15, 12};

    • 这是一个整型数组 widths,它存储了每一列的宽度设置值。数组中的每个值代表对应列的宽度(单位是字符的数量)。
    • 15, 15, 15 等表示前面列宽设置为 15 个字符,12, 12, 12 等表示后面列宽设置为 12 个字符。
  • for (int i = 0; i < widths.length; i++) { ... }

    • 这是一个循环,用于遍历 widths 数组中的每个元素。
    • widths.length 返回数组的长度(此处为 10),所以循环会执行 10 次,每次处理一个列的宽度。
  • sheet.setColumnWidth(i, widths[i] * 256);

    • i 是列索引,因此 i09(共10列)。
    • widths[i] 获取数组中当前索引位置的宽度值,将这个值乘以 256 以得到正确的列宽单位(字符宽度的 1/256)。
    • sheet.setColumnWidth(i, widths[i] * 256) 将依次设置每一列的宽度,确保列宽符合指定的字符数。

6.数据格式化

这段代码展示了如何在 Apache POI 中设置 Excel 单元格的数据格式,具体包括数字格式和日期格式的设置。以下是对每一部分的详细解释:

1. 设置数字格式

CellStyle numberStyle = workbook.createCellStyle();
DataFormat format = workbook.createDataFormat();
numberStyle.setDataFormat(format.getFormat("#,##0.00"));

解释:

  • CellStyle numberStyle = workbook.createCellStyle();

    • 这行代码创建一个新的 CellStyle 对象(numberStyle),该对象用于设置单元格的样式。
    • CellStyle 用于控制单元格的外观,比如字体、边框、对齐方式以及数据格式等。
  • DataFormat format = workbook.createDataFormat();

    • createDataFormat()Workbook 类的一个方法,用于创建一个 DataFormat 对象。DataFormat 类用于定义单元格的数据格式(如日期、数字、货币等)。
    • format 对象将用于设置不同类型的格式,允许将格式应用于 CellStyle 中。
  • numberStyle.setDataFormat(format.getFormat("#,##0.00"));

    • setDataFormat() 方法用于为 CellStyle 设置数据格式。
    • format.getFormat("#,##0.00") 使用 DataFormat 对象来定义具体的数字格式。这段格式表示:
      • #,##0.00:这是数字的格式模式,表示数字应该使用千位分隔符(,),并且保留两位小数(.00)。例如,数字 1234567.89 会显示为 1,234,567.89
      • # 是占位符,表示数字的每个位置(但如果没有值,它就不显示)。
      • 0 表示即使数字为零,也会显示该位置的零。
      • .00 表示保留两位小数,即使实际数据没有小数部分,也会显示为 0.00

2. 设置日期格式

CellStyle dateStyle = workbook.createCellStyle();
dateStyle.setDataFormat(format.getFormat("yyyy/mm/dd"));

解释:

  • CellStyle dateStyle = workbook.createCellStyle();

    • 这行代码创建一个新的 CellStyle 对象(dateStyle),用于设置日期单元格的格式。
    • 这个 CellStyle 对象将应用于日期格式的单元格。
  • dateStyle.setDataFormat(format.getFormat("yyyy/mm/dd"));

    • setDataFormat() 方法为 dateStyle 设置数据格式。
    • format.getFormat("yyyy/mm/dd") 使用 DataFormat 对象来设置日期的格式。具体格式为:
      • yyyy/mm/dd:这是日期的格式模式,表示日期应该显示为年-月-日的格式。
        • yyyy 表示四位年份(例如 2025)。
        • mm 表示两位月份(例如 01 表示一月,12 表示十二月)。
        • dd 表示两位日期(例如 01 表示第一天,31 表示最后一天)。
      • 例如,日期 2025/01/17 会显示为 2025/01/17

总结:

这段代码展示了如何为 Excel 单元格设置数字和日期格式,具体包括:

  1. 数字格式:将单元格内容设置为数字格式,使用千位分隔符并保留两位小数(例如:1,234,567.89)。
  2. 日期格式:将单元格内容设置为日期格式,显示为年-月-日(例如:2025/01/17)。

代码展示

1.POM依赖

POM依赖需要引入org.apache.poi

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.3</version>
        </dependency>

2.实体类Mode

BOM清单标题头

public class BOMHeader {
    private String orderNumber;        // 订单编号
    private String productNumber;      // 产品编号
    private String productName;        // 产品名称
    private String specification;      // 规格型号
    private Integer orderQuantity;     // 订单数量
    private String planStartDate;      // 计划投产日期
    private Integer productionCycle;   // 计划生产周期
    private String planEndDate;        // 计划截止日期
    private String deliveryDate;       // 订单交期
    private Double totalCost;          // 合计成本

    public BOMHeader(String orderNumber, String productNumber, String productName, String specification, Integer orderQuantity, String planStartDate, Integer productionCycle, String planEndDate, String deliveryDate, Double totalCost) {
        this.orderNumber = orderNumber;
        this.productNumber = productNumber;
        this.productName = productName;
        this.specification = specification;
        this.orderQuantity = orderQuantity;
        this.planStartDate = planStartDate;
        this.productionCycle = productionCycle;
        this.planEndDate = planEndDate;
        this.deliveryDate = deliveryDate;
        this.totalCost = totalCost;
    }

    public BOMHeader() {
    }

    public String getOrderNumber() {
        return orderNumber;
    }

    public void setOrderNumber(String orderNumber) {
        this.orderNumber = orderNumber;
    }

    public String getProductNumber() {
        return productNumber;
    }

    public void setProductNumber(String productNumber) {
        this.productNumber = productNumber;
    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public String getSpecification() {
        return specification;
    }

    public void setSpecification(String specification) {
        this.specification = specification;
    }

    public Integer getOrderQuantity() {
        return orderQuantity;
    }

    public void setOrderQuantity(Integer orderQuantity) {
        this.orderQuantity = orderQuantity;
    }

    public String getPlanStartDate() {
        return planStartDate;
    }

    public void setPlanStartDate(String planStartDate) {
        this.planStartDate = planStartDate;
    }

    public Integer getProductionCycle() {
        return productionCycle;
    }

    public void setProductionCycle(Integer productionCycle) {
        this.productionCycle = productionCycle;
    }

    public String getPlanEndDate() {
        return planEndDate;
    }

    public void setPlanEndDate(String planEndDate) {
        this.planEndDate = planEndDate;
    }

    public String getDeliveryDate() {
        return deliveryDate;
    }

    public void setDeliveryDate(String deliveryDate) {
        this.deliveryDate = deliveryDate;
    }

    public Double getTotalCost() {
        return totalCost;
    }

    public void setTotalCost(Double totalCost) {
        this.totalCost = totalCost;
    }
}

BOM清单列表

public class BOMItem {
    private String materialCode;    // 料号
    private String name;           // 品名
    private String modelNumber;    // 型号
    private String unit;           // 计量单位
    private Integer quantity;      // 用量
    private String manufacturer;   // 厂家/品牌
    private String channel;        // 途径/渠道
    private Double unitPrice;      // 单价
    private Double total;          // 合计
    private String remarks;        // 备注

    public BOMItem(String materialCode, String name, String modelNumber, String unit, Integer quantity, String manufacturer, String channel, Double unitPrice, Double total, String remarks) {
        this.materialCode = materialCode;
        this.name = name;
        this.modelNumber = modelNumber;
        this.unit = unit;
        this.quantity = quantity;
        this.manufacturer = manufacturer;
        this.channel = channel;
        this.unitPrice = unitPrice;
        this.total = total;
        this.remarks = remarks;
    }

    public BOMItem() {
    }

    public String getMaterialCode() {
        return materialCode;
    }

    public void setMaterialCode(String materialCode) {
        this.materialCode = materialCode;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getModelNumber() {
        return modelNumber;
    }

    public void setModelNumber(String modelNumber) {
        this.modelNumber = modelNumber;
    }

    public String getUnit() {
        return unit;
    }

    public void setUnit(String unit) {
        this.unit = unit;
    }

    public Integer getQuantity() {
        return quantity;
    }

    public void setQuantity(Integer quantity) {
        this.quantity = quantity;
    }

    public String getManufacturer() {
        return manufacturer;
    }

    public void setManufacturer(String manufacturer) {
        this.manufacturer = manufacturer;
    }

    public String getChannel() {
        return channel;
    }

    public void setChannel(String channel) {
        this.channel = channel;
    }

    public Double getUnitPrice() {
        return unitPrice;
    }

    public void setUnitPrice(Double unitPrice) {
        this.unitPrice = unitPrice;
    }

    public Double getTotal() {
        return total;
    }

    public void setTotal(Double total) {
        this.total = total;
    }

    public String getRemarks() {
        return remarks;
    }

    public void setRemarks(String remarks) {
        this.remarks = remarks;
    }
}

3.Controller层

import com.e.toexcel.model.BOMHeader;
import com.e.toexcel.model.BOMItem;
import com.e.toexcel.service.BOMExcelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/bom")
public class BOMController {
    @Autowired
    private BOMExcelService bomExcelService;

    @GetMapping("/export")
    public ResponseEntity<byte[]> exportBOM() {
        try {
            // 创建示例数据
            BOMHeader header = createSampleHeader();
            List<BOMItem> items = createSampleItems();

            // 生成Excel文件
            byte[] excelContent = bomExcelService.generateBOMExcel(header, items);

            // 设置响应头
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

            // 使用 URLEncoder 对文件名进行编码
            String filename = URLEncoder.encode("BOM物料清单.xlsx", StandardCharsets.UTF_8.name());
            // 替换空格编码
            filename = filename.replaceAll("\\+", "%20");
            // 设置 Content-Disposition 头
            headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + filename);

            return ResponseEntity.ok()
                    .headers(headers)
                    .body(excelContent);

        } catch (Exception e) {
            e.printStackTrace(); // 添加日志输出以便调试
            return ResponseEntity.internalServerError().build();
        }
    }

    private BOMHeader createSampleHeader() {
        return new BOMHeader(
                "BH00000002",
                "CP00000002",
                "自动包装机",
                "CS5506",
                80000,
                "2024/3/15",
                25,
                "2024/4/10",
                "2024/4/20",
                98560000.00
        );
    }

    private List<BOMItem> createSampleItems() {
        List<BOMItem> items = new ArrayList<>();
        // 添加所有示例数据
        items.add(createBOMItem("WL000101", "传送带", "ZJ000101", "个", 40000, "ABC品牌", "采购链接: yyyy", 120.00));
        items.add(createBOMItem("WL000102", "电机", "ZJ000102", "个", 80000, "DEF品牌", "自制生产", 0.00));
        items.add(createBOMItem("WL000103", "控制器", "ZJ000103", "个", 40000, "GHI品牌", "采购链接: yyyy", 350.00));
        items.add(createBOMItem("WL000104", "感应器", "ZJ000104", "个", 160000, "JKL品牌", "采购链接: yyyy", 80.00));
        items.add(createBOMItem("WL000105", "支架", "ZJ000105", "个", 80000, "MNO品牌", "自制生产", 0.00));
        items.add(createBOMItem("WL000106", "螺丝套件", "ZJ000106", "个", 320000, "PQR品牌", "采购链接: yyyy", 25.00));
        items.add(createBOMItem("WL000107", "线缆", "ZJ000107", "个", 240000, "STU品牌", "采购链接: yyyy", 45.00));
        items.add(createBOMItem("WL000108", "外壳", "ZJ000108", "个", 80000, "VWX品牌", "自制生产", 0.00));
        items.add(createBOMItem("WL000109", "显示屏", "ZJ000109", "个", 80000, "YZA品牌", "采购链接: yyyy", 280.00));
        items.add(createBOMItem("WL000110", "按钮组", "ZJ000110", "个", 160000, "BCD品牌", "采购链接: yyyy", 95.00));
        items.add(createBOMItem("WL000111", "密封圈", "ZJ000111", "个", 240000, "EFG品牌", "采购链接: yyyy", 35.00));
        items.add(createBOMItem("WL000112", "铭牌", "ZJ000112", "个", 80000, "HIJ品牌", "采购链接: yyyy", 15.00));
        items.add(createBOMItem("WL000113", "包装材料", "ZJ000113", "个", 80000, "KLM品牌", "采购链接: yyyy", 12.00));
        return items;
    }

    private BOMItem createBOMItem(String code, String name, String model, String unit, 
                                 int quantity, String manufacturer, String channel, double price) {
        double total = channel.equals("自制生产") ? 0.00 : price * quantity;
        return new BOMItem(
                code,
                name,
                model,
                unit,
                quantity,
                manufacturer,
                channel,
                price,
                total,
                ""  // remarks
        );
    }
} 

4.Service层

package com.e.toexcel.service;

import com.e.toexcel.model.BOMHeader;
import com.e.toexcel.model.BOMItem;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.List;

@Service
public class BOMExcelService {
    private CellStyle valueStyle;  // 添加成员变量

    private void createTitle(Sheet sheet, CellStyle style) {
        Row titleRow = sheet.createRow(1);
        
        // 为所有要合并的单元格创建样式
        for (int i = 0; i < 10; i++) {
            Cell cell = titleRow.createCell(i);
            cell.setCellStyle(style);
            // 只在第一个单元格设置值
            if (i == 0) {
                cell.setCellValue("BOM物料清单");
            }
        }
        
        // 合并单元格
        sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, 9));
    }

    private void createBasicInfo(Sheet sheet, BOMHeader header, CellStyle style) {
        // 第一行基本信息
        Row row1 = sheet.createRow(3);
        createHeaderCell(row1, 0, "订单编号", header.getOrderNumber(), style);
        createHeaderCell(row1, 2, "产品编号", header.getProductNumber(), style);
        createHeaderCell(row1, 4, "产品名称", header.getProductName(), style);
        createHeaderCell(row1, 6, "规格型号", header.getSpecification(), style);
        createHeaderCell(row1, 8, "订单数量", String.valueOf(header.getOrderQuantity()), style);

        // 第二行基本信息
        Row row2 = sheet.createRow(4);
        createHeaderCell(row2, 0, "计划投产日期", header.getPlanStartDate(), style);
        createHeaderCell(row2, 2, "计划生产周期", String.valueOf(header.getProductionCycle()), style);
        createHeaderCell(row2, 4, "计划截止日期", header.getPlanEndDate(), style);
        createHeaderCell(row2, 6, "订单交期", header.getDeliveryDate(), style);
        createHeaderCell(row2, 8, "合计成本", String.format("%.2f", header.getTotalCost()), style);
    }

    private void createHeaderCell(Row row, int col, String label, String value, CellStyle headerStyle) {
        Cell labelCell = row.createCell(col);
        labelCell.setCellValue(label);
        labelCell.setCellStyle(headerStyle);

        Cell valueCell = row.createCell(col + 1);
        valueCell.setCellValue(value);
        valueCell.setCellStyle(this.valueStyle);
    }

    private void createTableHeader(Sheet sheet, CellStyle style) {
        Row headerRow = sheet.createRow(6);
        String[] headers = {"料号", "品名", "型号", "计量单位", "用量", "厂家/品牌", "途径/渠道", "单价", "合计", "备注"};
        for (int i = 0; i < headers.length; i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headers[i]);
            cell.setCellStyle(style);
        }
    }

    private void fillData(Sheet sheet, List<BOMItem> items, CellStyle style) {
        int rowNum = 7;
        for (BOMItem item : items) {
            Row row = sheet.createRow(rowNum++);
            row.createCell(0).setCellValue(item.getMaterialCode());
            row.createCell(1).setCellValue(item.getName());
            row.createCell(2).setCellValue(item.getModelNumber());
            row.createCell(3).setCellValue(item.getUnit());
            row.createCell(4).setCellValue(item.getQuantity());
            row.createCell(5).setCellValue(item.getManufacturer());
            row.createCell(6).setCellValue(item.getChannel());
            row.createCell(7).setCellValue(item.getUnitPrice());
            row.createCell(8).setCellValue(item.getTotal());
            row.createCell(9).setCellValue(item.getRemarks());
            
            for (int i = 0; i < 10; i++) {
                row.getCell(i).setCellStyle(style);
            }
        }
    }

    private void createNote(Sheet sheet, CellStyle style) {
        Row noteRow = sheet.createRow(sheet.getLastRowNum() + 1);
        
        // 创建所有需要合并的单元格并设置样式
        for (int i = 0; i < 10; i++) {
            Cell cell = noteRow.createCell(i);
            cell.setCellStyle(style);
            // 只在第一个单元格设置值
            if (i == 0) {
                cell.setCellValue("说明:采购渠道请与采购部门进行确认,如物料采购困难,请及时与研发人员沟通更换其他替代品");
            }
        }
        
        // 合并单元格
        sheet.addMergedRegion(new CellRangeAddress(noteRow.getRowNum(), noteRow.getRowNum(), 0, 9));
    }

    private void setColumnWidths(Sheet sheet) {
        int[] widths = {15, 15, 15, 12, 12, 12, 15, 12, 15, 12};
        for (int i = 0; i < widths.length; i++) {
            sheet.setColumnWidth(i, widths[i] * 256);
        }
    }

    public byte[] generateBOMExcel(BOMHeader header, List<BOMItem> items) throws IOException {
        try (Workbook workbook = new XSSFWorkbook()) {
            Sheet sheet = workbook.createSheet("BOM物料清单");
            
            // 创建样式
            CellStyle headerStyle = createHeaderStyle(workbook);
            CellStyle normalStyle = createNormalStyle(workbook);
            this.valueStyle = createValueStyle(workbook);  // 初始化值样式
            
            // 设置标题
            createTitle(sheet, headerStyle);

            // 设置基本信息
            createBasicInfo(sheet, header, headerStyle);

            // 创建表头
            createTableHeader(sheet, headerStyle);

            // 填充数据
            fillData(sheet, items, normalStyle);

            // 添加说明
            createNote(sheet, normalStyle);

            // 调整列宽
            setColumnWidths(sheet);

            // 导出
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            workbook.write(outputStream);
            return outputStream.toByteArray();
        }
    }

    // 该方法用于创建 Excel 表头的单元格样式
    private CellStyle createHeaderStyle(Workbook workbook) {
        // 创建一个新的单元格样式
        CellStyle style = workbook.createCellStyle();
        // 设置单元格的前景填充颜色为浅绿
        style.setFillForegroundColor(IndexedColors.LIGHT_GREEN.getIndex());
        // 设置填充模式为纯色填充
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        // 设置上边框为细线
        style.setBorderTop(BorderStyle.THIN);
        // 设置下边框为细线
        style.setBorderBottom(BorderStyle.THIN);
        // 设置左边框为细线
        style.setBorderLeft(BorderStyle.THIN);
        // 设置右边框为细线
        style.setBorderRight(BorderStyle.THIN);
        // 设置水平对齐方式为居中
        style.setAlignment(HorizontalAlignment.CENTER);
        // 设置垂直对齐方式为居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        // 返回创建好的单元格样式
        return style;
    }

    // 该方法用于创建 Excel 普通单元格的样式
    private CellStyle createNormalStyle(Workbook workbook) {
        // 创建一个新的单元格样式
        CellStyle style = workbook.createCellStyle();
        // 设置上边框为细线
        style.setBorderTop(BorderStyle.THIN);
        // 设置下边框为细线
        style.setBorderBottom(BorderStyle.THIN);
        // 设置左边框为细线
        style.setBorderLeft(BorderStyle.THIN);
        // 设置右边框为细线
        style.setBorderRight(BorderStyle.THIN);
        // 设置水平对齐方式为居中
        style.setAlignment(HorizontalAlignment.CENTER);
        // 设置垂直对齐方式为居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        // 返回创建好的单元格样式
        return style;
    }

    // 此方法用于创建 Excel 中存储值的单元格的样式
    private CellStyle createValueStyle(Workbook workbook) {
        // 创建一个新的单元格样式对象
        CellStyle style = workbook.createCellStyle();
        // 设置上边框为细线
        style.setBorderTop(BorderStyle.THIN);
        // 设置下边框为细线
        style.setBorderBottom(BorderStyle.THIN);
        // 设置左边框为细线
        style.setBorderLeft(BorderStyle.THIN);
        // 设置右边框为细线
        style.setBorderRight(BorderStyle.THIN);
        // 设置单元格内容的水平对齐方式为居中
        style.setAlignment(HorizontalAlignment.CENTER);
        // 设置单元格内容的垂直对齐方式为居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        // 将创建好的单元格样式对象返回
        return style;
    }
} 

获取源码

源码地址:源码地址

写到这里也就结束了,如果你觉得此文章对你有所帮助的话就一键三连,在下谢谢您嘞!

标签:生成器,String,Spring,单元格,Excel,style,private,设置,public
From: https://blog.csdn.net/weixin_66401877/article/details/145217219

相关文章

  • 计算机毕业设计—95185 springboot危险品监管系统(源码免费领)
    摘要第1章绪论1.1研究背景1.2 研究现状1.3论文结构与章节安排第2章相关技术2.1开发技术2.2Java简介2.3 MVVM模式2.4 B/S结构2.5  MySQL数据库2.6 SpringBoot框架介绍第3章系统分析3.1可行性分析3.2系统流程分析3.2.1数据增加......
  • (2024最新毕设合集)基于SpringBoot的游乐园管理系统-69394|可做计算机毕业设计JAVA、PHP
    目录1绪论1.1选题背景与意义1.2国内外研究现状1.3论文结构与章节安排2系统分析2.1可行性分析2.1.1经济可行性2.1.2技术可行性2.1.3操作可行性2.2系统流程分析2.2.1系统开发流程2.2.2用户登录流程2.2.3系统操作流程2.2.4添加信息流程2.2.5......
  • 基于springboot的微竞网咖网吧管理系统
    一、系统简介“基于SpringBoot的微竞网咖网吧管理系统”是一款为网吧运营而设计的信息化管理系统,旨在利用SpringBoot框架的优势,实现对网吧的高效、智能管理。该系统可以全面覆盖网吧日常运营中的各项事务,提升网吧的管理水平和用户体验。二、功能模块用户管理:对......
  • 基于java+springboot的网络选课管理系统
    一、系统概述“基于Java+SpringBoot的网络选课管理系统”是一个利用Java编程语言和SpringBoot框架开发的综合性平台,旨在为学校提供一个方便、高效、灵活的课程选择管理解决方案。二、功能特点用户管理:系统可区分不同角色,如学生、教师和管理员。学生可注册登录......
  • SpringBoot中的Undertow容器和tomcat容器
      在SpringBoot框架中,我们使用最多的是Tomcat,这是SpringBoot默认的容器技术,而且是内嵌式的Tomcat。同时,SpringBoot也支持Undertow容器,我们可以很方便的用Undertow替换Tomcat,而Undertow的性能和内存使用方面都优于Tomcat。1SpringBoot中的Tomcat容器SpringBoot可以说是目前最......
  • 基于springboot+vue的推荐算法的智能快递分拣系统(源码+文档+部署讲解等)
    课题简介该系统旨在利用SpringBoot和Vue技术,结合推荐算法,构建一个智能快递分拣系统,以提高快递分拣的效率和准确性。通过对快递信息(如重量、尺寸、目的地、发货地等)的分析,将包裹分配到最适合的分拣路径和目的地,实现自动化和智能化的分拣操作。二、系统功能模块快递......
  • 基于springboot+vue的推荐算法的校园电子图书听书系统的设计与实现(源码+文档+部署讲
    课题简介一、系统概述本系统旨在为校园用户提供一个电子图书听书平台,结合SpringBoot和Vue的技术优势,并融入推荐算法,提升用户的听书体验。它将满足学生和教师在学习、休闲等方面的听书需求,同时提供个性化的推荐服务。二、系统功能模块图书资源管理模块:存储和管理......
  • 从零开始:Spring Boot核心概念与架构解析
    引言在当今的Java开发领域,SpringBoot已经成为构建企业级应用的首选框架之一。它以其简洁、高效、易于上手的特点,极大地简化了Spring应用的开发过程。本文将从SpringBoot的核心概念入手,深入解析其架构设计和运行原理,帮助读者从零开始全面理解SpringBoot。一、SpringBoot......
  • 计算机毕业设计Springboot流浪动物救助系统 Springboot宠物领养与救助管理系统 基于Sp
    计算机毕业设计Springboot流浪动物救助系统o8g44kwc(配套有源码程序mysql数据库论文)本套源码可以先看具体功能演示视频领取,文末有联xi可分享随着社会的进步和人们生活水平的提高,越来越多的人开始关注流浪动物的生存状况。流浪动物数量庞大、分布广泛,传统的人工救助模式已......
  • 计算机毕业设计Springboot猫咖管理系统 基于Spring Boot的猫咖信息化管理平台设计与实
    计算机毕业设计Springboot猫咖管理系统6pd25bgn(配套有源码程序mysql数据库论文)本套源码可以先看具体功能演示视频领取,文末有联xi可分享随着互联网技术的飞速发展,传统行业逐渐向信息化、智能化转型。猫咖作为一种新兴的休闲场所,结合了猫咪互动与咖啡饮品的双重魅力,吸引了......