项目中有个需求,根据计划单号进行对应的单元格合并,还有对应的导出功能也是需要,前台使用的是bootstrap插件,还好bootstrap有自己的合并功能。
下面看下具体的实现
一、前台bootstap实现动态合并单元格
1.在onLoadSuccess : function(data) {
}方法中进行实现合并单元格,注意,一定是其他操作完成之后再合并单元格,我的是有合计功能,刚开始是先合并再实现合计,结果合计功能一直出不来,很纳闷,试了一下更改顺序,结果就出来了,把整个的代码都贴一下吧
function loadTable(flag) {
if(flag=="1")
{
searchStr = $(".search__inp").val().trim();
}
var pageNumber = 1;
$("#mainTable").bootstrapTable("destroy");
var options = {
pagination : true
//, height: 400
,
showFooter : false,
paginationLoop : false
//,sortable: true
,
cache : false,
pageNumber : pageNumber,
pageSize : 10,
pageList : [ 10],//[ 10, 20, 50 ],
method : "get",
contentType : 'application/x-www-form-urlencoded; charset=UTF-8',
url : basePath + "student/productionReport/getReportList",
queryParamsType : "",
sidePagination : "server",
queryParams : queryParamsMain,
columns : [[
{
field : "id",
title : "序号",
align : "center",
valign : "middle",
colspan: 1,
rowspan: 2,
formatter : function(value, row, index) {
//获取每页显示的数量
var pageSize = $('#mainTable').bootstrapTable(
'getOptions').pageSize;
//获取当前是第几页
var pageNumber = $('#mainTable').bootstrapTable(
'getOptions').pageNumber;
//返回序号,注意index是从0开始的,所以要加上1
if (value == "total") {
return "合计";
} else {
return pageSize * (pageNumber - 1) + index + 1;
}
},
//width : 40
},
{
field : "planType",
title : "生产类型",
align : "center",
valign : "middle",
colspan: 1,
rowspan: 2,
formatter : function(value, row, index) {
var str="";
if(value==1)
{
str="内部生产";
}
else if(value==2)
{
str="委托加工";
}
return str;
},
//width : 68
},
{
field : "planCode",
title : "生产计划单号",
align : "center",
valign : "middle",
colspan: 1,
rowspan: 2,
formatter : function(value, row, index) {
return value;
},
//width : 58
},
{
//field : "code",
title : "生产信息",
align : "center",
valign : "middle",
colspan: 4,
rowspan: 1
//width : 145
},
{
//field : "code",
title : "用料明细",
align : "center",
valign : "middle",
colspan: 4,
rowspan: 1
//width : 145
},
{
field : "roundNo",
title : "轮次",
align : "center",
valign : "middle",
colspan: 1,
rowspan: 2,
//sortable: true,
formatter : function(value, row, index) {
if (value != "") {
return "第" + value + "轮";
} else {
return "";
}
},
//width : 46
}
],
[{
field : "productName",
title : "产品名称",
align : "center",
valign : "middle",
formatter : function(value, row, index) {
return value;
},
//width : 67
},
{
field : "productCode",
title : "型料号",
align : "center",
valign : "middle",
formatter : function(value, row, index) {
return value;
},
//width : 73
},
{
field : "actualQuantity",
title : "实际生产数量",
align : "center",
valign : "middle",
formatter : function(value, row, index) {
return value;
},
//width : 28
},
{
field : "qualificationRate",
title : "合格率(%)",
align : "center",
valign : "middle",
formatter : function(value, row, index) {
return value;
},
//width : 28
},
{
field : "bomType",
title : "产品类型",
align : "center",
valign : "middle",
formatter : function(value, row, index) {
switch(value)
{
case 1:value="产成品";break;
case 2:value="半成品";break;
case 3:value="原材料";break;
}
return value;
},
//width : 53
},
{
field : "bomProductName",
title : "产品名称",
align : "center",
valign : "middle",
formatter : function(value, row, index) {
return value;
},
//width : 86
},
{
field : "bomProductCode",
title : "型料号",
align : "center",
valign : "middle",
formatter : function(value, row, index) {
return value;
},
//width : 190
},
{
field : "bomQuantity",
title : "生产实际用量",
align : "center",
valign : "middle",
formatter : function(value, row, index) {
return value;
},
//width : 65
}
]],
onLoadSuccess : function(data) {
var sum_1 = 0;
var sum_2 = 0;
var planCode="";
for ( var o in data.rows) {
if (!isNaN(o)) {
if(data.rows[o].planCode!=planCode)
{
if(!isNaN(data.rows[o].actualQuantity))
{
sum_1 = parseFloat(sum_1)+ parseFloat(data.rows[o].actualQuantity);
}
}
sum_2 = parseFloat(sum_2)+ parseFloat(data.rows[o].bomQuantity);
planCode=data.rows[o].planCode;
}
}
if(data.rows.length>0)
{
var rows = [];
rows.push({
id : "total",
planType : "",
planCode : "",
productName : "",
productCode : "",
actualQuantity : sum_1,
qualificationRate : "",
bomType : "",
bomProductName : "",
bomProductCode : "",
bomQuantity : sum_2,
roundNo : ""
});
$('#mainTable').bootstrapTable('append', rows);
}
mergeCells(data.rows,"planCode", "planCode", 1, $('#mainTable'));
mergeCells(data.rows,"planCode", "actualQuantity", 1, $('#mainTable'));
mergeCells(data.rows,"planCode", "productName", 1, $('#mainTable'));
mergeCells(data.rows,"planCode", "productCode", 1, $('#mainTable'));
mergeCells(data.rows,"planCode", "qualificationRate", 1, $('#mainTable'));
},
responseHandler : function(data) {
return {
total : data.totalCount,
rows : data.result,
};
},
onPageChange : function(number, size) {
pageNumber = number;
},
onLoadError : function(textStatus, XMLHttpRequest) {
}
};
$("#mainTable").bootstrapTable(options);
}
View Code
2.具体的进行单元格合并,mergeCells可以实现单元格合并,此功能还是非常简单的
1 /**
2 * 合并单元格
3 * @param data 原始数据(在服务端完成排序)
4 * @param fieldName 合并属性名称
5 * @param colspan 合并列
6 * @param target 目标表格对象
7 */
8 function mergeCells(data,exhibitionName,fieldName,colspan,target){
9 //声明一个map计算相同属性值在data对象出现的次数和
10 var sortMap = {};
11 for(var i = 0 ; i < data.length ; i++){
12 for(var prop in data[i]){
13 if(prop == exhibitionName){
14 var key = data[i][prop];
15 if(sortMap.hasOwnProperty(key)){
16 sortMap[key] = sortMap[key] * 1 + 1;
17 } else {
18 sortMap[key] = 1;
19 }
20 break;
21 }
22 }
23 }
24
25 var index = 0;
26 for(var prop in sortMap){
27 var count = sortMap[prop] * 1;
28 $("#mainTable").bootstrapTable('mergeCells',{index:index, field:fieldName, colspan: colspan, rowspan: count});
29 index += count;
30 }
31 }
View Code
二、导出excle中实现动态单元格合并
1、在pom.xml中引入jar包
1 <!-- exel导出依赖 -->
2 <dependency>
3 <groupId>net.sf.jxls</groupId>
4 <artifactId>jxls-reader</artifactId>
5 <version>0.9.9</version>
6 </dependency>
7 <dependency>
8 <groupId>net.sf.jxls</groupId>
9 <artifactId>jxls-core</artifactId>
10 <version>0.9.9</version>
11 </dependency>
12 <dependency>
13 <groupId>jexcelapi</groupId>
14 <artifactId>jxl</artifactId>
15 <version>2.4.2</version>
16 </dependency>
View Code
2、业务功能实现,
3、导出方法(合并单元格),含有非合并的代码
List<Merge> ml = getMerge(list, "getPlanCode");这个是合并的关键字
// 起始行号,终止行号, 起始列号,终止列号
sheet.addMergedRegion(new CellRangeAddress(m.getFromRow() + 3, m.getToRow() +3, 3, 3));这个进行具体的合并,从网上找的帖子,用的反射降低耦合度,其实可以不用反射,直接用对应功能实现
1 package com.gta.scm.component.common.utils;
2
3 /**
4 *
5 */
6
7
8 import java.io.BufferedInputStream;
9 import java.io.ByteArrayOutputStream;
10 import java.io.FileInputStream;
11 import java.io.FileNotFoundException;
12 import java.io.IOException;
13 import java.io.InputStream;
14 import java.io.OutputStream;
15 import java.io.UnsupportedEncodingException;
16 import java.lang.reflect.InvocationTargetException;
17 import java.util.ArrayList;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21 import javax.servlet.ServletOutputStream;
22 import javax.servlet.http.HttpServletRequest;
23 import javax.servlet.http.HttpServletResponse;
24
25 import net.sf.jxls.exception.ParsePropertyException;
26 import net.sf.jxls.transformer.XLSTransformer;
27 import org.apache.commons.codec.binary.Base64;
28 import org.apache.poi.hssf.usermodel.HSSFSheet;
29 import org.apache.poi.hssf.usermodel.HSSFWorkbook;
30 import org.apache.poi.ss.util.CellRangeAddress;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 import com.fasterxml.jackson.databind.exc.InvalidFormatException;
35 import com.gta.scm.common.entity.Merge;
36
37
38 /**
39 * @author jingshu.deng
40 *
41 */
42 public class FileUtils {
43 private static Logger logger = LoggerFactory.getLogger(FileUtils.class);
44
45 /**
46 * @param fileName
47 * @return
48 */
49 public static String getFileExtension(String fileName) {
50 if (StringUtils.isEmpty(fileName)) {
51 return StringUtils.EMPTY;
52 }
53
54 return StringUtils.substringAfter(fileName, ".");
55 }
56
57 /**
58 *
59 * 解决文件下载时文件名的中文乱码及Firefox下空格被截断的问题
60 *
61 * @param request
62 * @param fileName
63 * @return
64 */
65 public static String decodeFileName(HttpServletRequest request, String fileName) {
66 String agent = (String) request.getHeader(CharEncodingCons.USER_AGENT);
67 try {
68 // check whether Firefox according to USER-AGENT
69 // 增加条件 agent.indexOf("Trident") == -1 针对IE10以上
70 if (agent != null && agent.indexOf(CharEncodingCons.BROWSER_IE) == -1 && agent.indexOf("Trident") == -1) {
71 byte[] bytes = Base64.encodeBase64(fileName.getBytes(CharEncodingCons.HTTP_CHARSET));
72 fileName = new String(bytes, CharEncodingCons.HTTP_CHARSET);
73 fileName = "=?" + CharEncodingCons.HTTP_CHARSET + "?B?" + fileName + "?=";
74 }
75 else {
76 byte[] bytes = fileName.getBytes(CharEncodingCons.DOWNLOAD_CHARSET);
77 fileName = new String(bytes, CharEncodingCons.WEB_CHARSET);
78 }
79 }
80 catch (UnsupportedEncodingException e) {
81 logger.error("UnsupportedEncodingException occurs while flushing file", e);
82 }
83 return fileName;
84 }
85
86 /**
87 * 导出Excel文件,按模板方式
88 * @param response
89 * @param templateName 模板名称及完全路径
90 * @param map
91 * @param fileName 要导出的文件名
92 */
93 public static void exportXlsFile(HttpServletRequest request,HttpServletResponse response,String templateName,Map<String,Object> map,String fileName){
94 try
95 {
96 XLSTransformer transformer=new XLSTransformer();
97 //XLSTransformer transformer = new XLSTransformer();
98 InputStream in = new BufferedInputStream(new FileInputStream(templateName),25000);
99 HSSFWorkbook workbook = transformer.transformXLS(in, map);
100 //write to buffer
101 ByteArrayOutputStream buf = new ByteArrayOutputStream(40000);
102 workbook.write(buf);
103 // write to response
104 in.close();
105 response.setContentType("application/vnd.ms-excel");
106 response.setHeader("Content-disposition", "attachment; filename=" + decodeFileName(request,fileName));
107 ServletOutputStream out = response.getOutputStream();
108 out.write(buf.toByteArray());
109 out.flush();
110 out.close();
111 }catch (IOException e){
112 e.printStackTrace();
113 }
114 }
115
116 // 先用String
117 public static <T> Object useMethod(T t, String sx) throws IllegalAccessException, IllegalArgumentException,
118 InvocationTargetException, NoSuchMethodException, SecurityException {
119 // 一般传入get方法
120 return (Object) t.getClass().getMethod(sx, null).invoke(t, null);
121
122 }
123
124 public static <T> List<Merge> getMerge(List<T> list, String sx) throws IllegalAccessException,
125 IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
126 // 可以传入 想合并的属性值 传入一个字符串 用反射找到相应的get方法 指定调用此方法。。这里先写死
127 List<Merge> ml = new ArrayList<Merge>();
128 for (int i = 0; i < list.size() - 1; i++) {
129 if (useMethod(list.get(i), sx).equals(useMethod(list.get(i + 1), sx))) {
130 Object property = useMethod(list.get(i), sx);
131 logger.debug("property"+property);
132 Merge merge = new Merge();
133 int fromRow = i, toRow = i + 1;
134 if (i + 2 < list.size()) {
135 for (int j = i + 2; j < list.size(); j++) {
136 if (useMethod(list.get(j), sx).equals(property) ) {
137 toRow++;
138 } else {
139 i = j - 1;
140 break;
141 }
142 }
143 }
144 merge.setFromRow(fromRow);
145 merge.setToRow(toRow);
146 ml.add(merge);
147 }
148 }
149 return ml;
150 }
151
152 public static <T> void exportXlsFileMerge(HttpServletRequest request,HttpServletResponse response,String templateName,List<T> list,Map<String,Object> map,String fileName)
153 throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException,
154 SecurityException, FileNotFoundException, ParsePropertyException, InvalidFormatException {
155 String templateFile = fileName;
156 Map<String, Object> beans = new HashMap<String, Object>();
157 InputStream in = new FileInputStream(templateName);
158
159 XLSTransformer transformer = new XLSTransformer();
160
161 HSSFWorkbook workBook = (HSSFWorkbook) transformer.transformXLS(in, map); // 传入模板的输入流和map
162 // 开始进行合并单元格
163 HSSFSheet sheet = workBook.getSheetAt(0);// 1 1
164 List<Merge> ml = getMerge(list, "getPlanCode");
165 for (Merge m : ml) {
166 // 起始行号,终止行号, 起始列号,终止列号
167 sheet.addMergedRegion(new CellRangeAddress(m.getFromRow() + 3, m.getToRow() +3, 3, 3));
168 sheet.addMergedRegion(new CellRangeAddress(m.getFromRow() + 3, m.getToRow() +3, 4, 4));
169 sheet.addMergedRegion(new CellRangeAddress(m.getFromRow() + 3, m.getToRow() +3, 5, 5));
170 sheet.addMergedRegion(new CellRangeAddress(m.getFromRow() + 3, m.getToRow() +3, 6, 6));
171 sheet.addMergedRegion(new CellRangeAddress(m.getFromRow() + 3, m.getToRow() +3, 2, 2));
172 }
173 try {
174
175 ByteArrayOutputStream buf = new ByteArrayOutputStream(40000);
176 workBook.write(buf);
177 // write to response
178 in.close();
179 response.setContentType("application/vnd.ms-excel");
180 response.setHeader("Content-disposition", "attachment; filename=" + decodeFileName(request,fileName));
181 ServletOutputStream out = response.getOutputStream();
182 out.write(buf.toByteArray());
183 out.flush();
184 out.close();
185
186
187 } catch (IOException ie) {
188 ie.printStackTrace();
189 } catch (ParsePropertyException e) {
190 // TODO Auto-generated catch block
191 e.printStackTrace();
192 }
193
194 }
195
196 }
View Code
添加merge类进行记录行号
1 public class Merge {
2 private int fromRow;
3 private int toRow;
4 private int fromIndex;
5 private int toIndex;
6 public int getFromRow() {
7 return fromRow;
8 }
9 public void setFromRow(int fromRow) {
10 this.fromRow = fromRow;
11 }
12 public int getToRow() {
13 return toRow;
14 }
15 public void setToRow(int toRow) {
16 this.toRow = toRow;
17 }
18 public int getFromIndex() {
19 return fromIndex;
20 }
21 public void setFromIndex(int fromIndex) {
22 this.fromIndex = fromIndex;
23 }
24 public int getToIndex() {
25 return toIndex;
26 }
27 public void setToIndex(int toIndex) {
28 this.toIndex = toIndex;
29 }
30
31 }
View Code
调用的地方需要特别注意的地方
String templateFileName = request.getServletContext().getRealPath("/")
+ "/reportTemplate//ProductionReportTemplate.xls";
String destFileName = "生产产品明细汇总表.xls";
map.put("productionReportList", productionReport);
4.exlce模板
5.导出结果
哈,这样前后台都进行了实现了