需求:在一个工作簿中,需要填充固定字段信息,并写入多个不同的标题列的表格及内容。
常规Excel写入一般是一个工作簿一个表头。
目录
一、复杂表单分析
1.表单示例
2.复杂表单拆解
示例的模板,可以拆解为6个组成部分:
(1)1-7行:表格固定的部分,需要在指定的单元格动态填充信息
(2)8-10行:表格动态写入的部分,由【工单器材】的字段列标题和内容组成。
(3)11-13行:表格动态写入的部分,由【工单服务】的字段列标题和内容组成。
(4)14行:表格动态写入的部分,由【工单结论】的字段列标题组成
(5)15-16行:表格动态写入的部分,由【费用计算】的字段列标题组成
(6)17-19行:表格固定的部分,但是因为无法往表格中间插入table,所以此处只能将固定的格式数据以table方法写入,由【表格固定结尾】的字段内容组成。
3.准备模板
因为表格的前7行是固定格式的,为了简化操作,我们直接将格式定好作为模板。而余下的行是根据数据量动态写入的。
那么,我们在代码中需要对该模板进行填充和写入的操作。
二、EasyExcel文档
有了以上思路,开始翻阅easyExcel文档,查找可以使用的方法。最终确定使用以下两个方法去实现。
1.最简单的填充Excel
用于填充固定模板中的指定字段。
2.使用table去写入
用于写入动态的标题和内容。
三、代码示例
1.实体类
工单模板的信息来源于工单信息、工单申请信息、工单设备申请明细、工单服务申请明细表。
1)工单信息表
/**
* 工单信息表
*/
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@Data
public class WorkOrderInfo implements Serializable {
// 工单ID
private Long workOrderId;
// 工单号码
private String workOrderCode;
// 发起人名称
private String creatorName;
// 处理人名称
private String handerName;
// 工单标题
private String workOrderTitle;
// 工单内容
private String workOrderContent;
// 工单结论
private String workOrderResult;
}
2)工单申请信息表
/**
* 工单申请信息表
*/
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@Data
public class WorkOrderApplyInfo implements Serializable {
// 工单申请id
private Long workOrderApplyId;
// 工单id
private Long workOrderId;
// 工单上门次数
private String visitNum;
// 工单单次支付(元)
private String singlePay;
// 工单总计费用(元)
private String totalPay;
// 设备申请明细
private List<WorkOrderDevice> deviceList;
// 服务申请明细
private List<WorkOrderService> serviceList;
}
3)工单设备申请明细表
/**
* 工单设备申请明细表
*/
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@Data
public class WorkOrderDevice implements Serializable {
// 工单设备申请明细id
private Long workOrderDeviceId;
// 工单id
private Long workOrderId;
// 工单申请id
private Long workOrderApplyId;
// 设备名称
private String deviceName;
// 设备明数
private String deviceContent;
// 申请数量
private String applyNum;
}
4)工单服务申请表
/**
* 工单服务申请明细表
*/
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@Data
public class WorkOrderService implements Serializable {
// 工单设备申请明细id
private Long workOrderServiceId;
// 工单id
private Long workOrderId;
// 工单申请id
private Long workOrderApplyId;
// 服务名称
private String serviceName;
// 服务描述
private String serviceContent;
// 申请工时(时)
private String hourNum;
}
2.工具类
1)内容导出工具类
因为该模板中不同的table,标题不同,占用合并的列数、对应字段也不同。所以此处将设备、服务、工单结论、费用合计、固定结尾的【标题】、【字段映射列及列数】分别单独设置。最后通过【字段映射列及列数】和实际数据,构造【内容】列。
tips:
excel写入实际上是一行一行的写入数据
一行的数据结构是List<String>
多行的数据结构是List<List<String>>
/**
* 工单确认单内容导出工具类
*/
@Component
@Slf4j
public class WorkOrderDataUtils {
// >>>>>>>>>>>>>>> 标题构造 >>>>>>>>>>>>>>>>>>>>>>>>>>>
/**
* 设备列表标题
* @return
*/
public static List<List<String>> deviceHead() {
List<List<String>> list = new ArrayList<List<String>>();
List<String> head0 = new ArrayList<String>();
head0.add("工单器材");
// 占用3列
list.add(head0);
list.add(head0);
list.add(head0);
List<String> head1 = new ArrayList<String>();
head1.add("器材描述");
// 占用2列
list.add(head1);
list.add(head1);
List<String> head2 = new ArrayList<String>();
head2.add("数量");
// 占用两列
list.add(head2);
list.add(head2);
return list;
}
/**
* 服务列表标题
* @return
*/
public static List<List<String>> serviceHead() {
List<List<String>> list = new ArrayList<List<String>>();
List<String> head0 = new ArrayList<String>();
head0.add("工单服务");
// 占用3列
list.add(head0);
list.add(head0);
list.add(head0);
List<String> head1 = new ArrayList<String>();
head1.add("服务描述");
// 占用2列
list.add(head1);
list.add(head1);
List<String> head2 = new ArrayList<String>();
head2.add("工时");
// 占用两列
list.add(head2);
list.add(head2);
return list;
}
/**
* 工单结论标题:
* 将结论传入作为标题
* @return
*/
public static List<List<String>> workOrderResultHead(String workOrderResult) {
List<List<String>> list = new ArrayList<List<String>>();
List<String> head0 = new ArrayList<String>();
head0.add("工单结论");
// 占用3列
list.add(head0);
list.add(head0);
list.add(head0);
List<String> head1 = new ArrayList<String>();
head1.add(workOrderResult);
// 占用4列
list.add(head1);
list.add(head1);
list.add(head1);
list.add(head1);
return list;
}
/**
* 费用合计标题
* 将上门次数,每次支付(元),总费用传入作为标题
* @return
*/
public static List<List<String>> totalHead(String visitNum, String singlePay, String totalPay) {
List<List<String>> list = new ArrayList<List<String>>();
List<String> head0 = new ArrayList<String>();
head0.add("上门次数");
head0.add(visitNum);
// 占用3列
list.add(head0);
list.add(head0);
list.add(head0)