目录
引言
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行的行号是1Row 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
枚举包括常见颜色,如 YELLOW
、RED
、BLUE
等。
setFillPattern(FillPatternType.SOLID_FOREGROUND)
:设置单元格的填充模式为 SOLID_FOREGROUND
,表示填充整个单元格背景色。FillPatternType
还可以选择其他模式,如 NO_FILL
(不填充)、SOLID_FOREGROUND
(完全填充)等。
颜色解释:
LIGHT_GREEN
是 IndexedColors
中的一个颜色常量,代表一种浅绿色。可以将单元格的背景色设置为浅绿色,增加表格的视觉效果,使其更具可读性和美观。
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()
获取了 IndexedColors
中 RED
(红色)的颜色索引。除了 RED
,IndexedColors
还包含多种颜色,如 BLACK
、BLUE
、GREEN
等。
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
列。 - 这两个单元格分别应用不同的样式(
labelStyle
和valueStyle
),从而使标签和值的显示效果有所区别。标签单元格可能使用不同的字体、颜色、背景等样式,而值单元格则可能有不同的格式。
使用示例:
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
是列索引,因此i
从0
到9
(共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,234,567.89
)。 - 日期格式:将单元格内容设置为日期格式,显示为年-月-日(例如:
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 {@Autowiredprivate 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;}
}
获取源码
源码地址:源码地址
写到这里也就结束了,如果你觉得此文章对你有所帮助的话就一键三连,在下谢谢您嘞!