项目需求,需要将页面上的报表导出Word文档。

一、报表如下:

image-20230224101902557

二、主要实现代码

1.导出Util类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package com.yhksxt.util;


import java.io.IOException;
import java.math.BigInteger;

import javax.servlet.http.HttpServletResponse;

import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTbl;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STMerge;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;


public class ExprotSTUtil {





public void exportSTToWord(String titleName,String[][] list ,HttpServletResponse response) throws IOException{
//创建document对象
XWPFDocument document = new XWPFDocument();

//添加标题
XWPFParagraph titleParagraph = document.createParagraph();
//设置段落居中
titleParagraph.setAlignment(ParagraphAlignment.CENTER);
XWPFRun titleParagraphRun = titleParagraph.createRun();
titleParagraphRun.setText(titleName);
titleParagraphRun.setColor("000000");
titleParagraphRun.setFontSize(20);
//创建表格
int row = list.length +2;
int column = list[0].length +3;
XWPFTable table = document.createTable(row,column);
setTableWidth(table, "10000");

//处理表达合并和数据填充

//合并列
mergeCellsVertically(table, 0, 0, 1);
mergeCellsVertically(table, 2, 0, 1);
mergeCellsVertically(table, column-1, 0, 1);

//合并行
for(int i=0;i<row-2;i++){
mergeCellsHorizontal(table, i, 0, 1);
mergeCellsHorizontal(table, i, 2, 4);
}

mergeCellsHorizontal(table, 0, 5, column-2);
mergeCellsHorizontal(table, row-2, 0, 4);
mergeCellsHorizontal(table, row-1, 0, 4);
mergeCellsHorizontal(table, row-1, 5, column-1);

//填充数据
XWPFTableRow rowIndex = table.getRow(0);
XWPFTableCell cell = rowIndex.getCell(0);
cell.setText(list[0][0]);
XWPFTableCell cell1 = rowIndex.getCell(2);
cell1.setText(list[0][1]);
XWPFTableCell cell2 = rowIndex.getCell(5);
cell2.setText("试题类型及题量");
XWPFTableCell cell3 = rowIndex.getCell(column-1);
cell3.setText(list[0][list[0].length-1]);

XWPFTableRow rowIndex1 = table.getRow(1);
for(int j=5,i=2;j<column-1;j++,i++){
XWPFTableCell cell4 = rowIndex1.getCell(j);
cell4.setText(list[0][i]);
}

for(int i = 2,ii=1;i<row-1;i++,ii++){
XWPFTableRow rowIndex2 = table.getRow(i);
for(int j = 0,jj=0;j<column-1 ;j++){
XWPFTableCell cell4 =null;
if(j==0){
cell4 =rowIndex2.getCell(j);
cell4.setText(list[ii][jj]);
jj++;
}if(j==2){
cell4 =rowIndex2.getCell(j);
cell4.setText(list[ii][jj]);
jj++;
}else if(j>=5){
cell4 =rowIndex2.getCell(j);
cell4.setText(list[ii][jj]);
jj++;
}
}
}

XWPFTableRow rowIndex3 = table.getRow(row-1);
XWPFTableCell cell5 =rowIndex3.getCell(0);
cell5.setText("其他需要说明的问题");

document.write(response.getOutputStream());
}


/***
* 跨行合并
* @param table
* @param col 合并列
* @param fromRow 起始行
* @param toRow 终止行
*/
private void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
if ( rowIndex == fromRow ) {
// The first merged cell is set with RESTART merge value
cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
} else {
// Cells which join (merge) the first one, are set with CONTINUE
cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
}
}
}

/***
* 跨列合并
* @param table
* @param row 所合并的行
* @param fromCell 起始列
* @param toCell 终止列
*/
private void mergeCellsHorizontal(XWPFTable table, int row, int fromCell, int toCell) {
for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {
XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
if ( cellIndex == fromCell ) {
// The first merged cell is set with RESTART merge value
cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
} else {
// Cells which join (merge) the first one, are set with CONTINUE
cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
}
}
}


/***
* 导出word 设置行宽
* @param table
* @param width
*/
private void setTableWidth(XWPFTable table,String width){
CTTbl ttbl = table.getCTTbl();
CTTblPr tblPr = ttbl.getTblPr() == null ? ttbl.addNewTblPr() : ttbl.getTblPr();
CTTblWidth tblWidth = tblPr.isSetTblW() ? tblPr.getTblW() : tblPr.addNewTblW();
CTJc cTJc=tblPr.addNewJc();
cTJc.setVal(STJc.Enum.forString("center"));
tblWidth.setW(new BigInteger(width));
tblWidth.setType(STTblWidth.DXA);
}
}

2.Controller中测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@RequestMapping(value = "exprotWord")
public void exprotWord(HttpServletRequest request,HttpServletResponse response,String judge,String titleName,String subjectId)
throws IOException , Exception {
String fileName =null;
String[][] list = null;
System.out.println(judge);
if(judge.equals("tlbb")){
list = questionService.getSubjectZhangTotal(subjectId);//页面所有显示内容
titleName = titleName + "题量报表";
}else if(judge.equals("blbb")){
String[][] tllist = questionService.getSubjectZhangTotal(subjectId);
list = questionService.getxkblbbBytlbb(tllist);//转成比例
titleName = titleName + "比例报表";
}else {
return;
}
fileName = titleName+".docx";
// 设置文件MIME类型
response.setContentType(request.getServletContext().getMimeType(fileName));
// 设置Content-Disposition
response.setHeader("Content-Disposition",
"attachment; fileName=" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));
ExprotSTUtil exprotWord = new ExprotSTUtil();
exprotWord.exportSTToWord(titleName, list, response);
}

三、导出效果

image-20230224102050188

四、值得讨论问题

1.报表通过jsp页面显示,数据存储问题。

​ 报表牵涉到对数据的统计,往往比较复杂。本列中,对每个学科下,Question表中所有题统计。牵涉到Chapter表中改学科对应的章节、Theme表中该学科对应的题型。每个题又有专属的学科、章节。并且学科中的题型、章节都是管理员可以增添删除的,所有数据都是动态的,这也就增加了处理的难度。在不确定题都所属哪个章节、哪个章节下的题型。同时也是报表所需,必须统计所有该章节,题型。查出的题后台处理后添加。 业务逻辑相对复杂一点,所以最后不要把数据都传到前端,在前端处理。所以,我采取把所有显示看到的数据,后台处理,存储到二维数组中,前台遍历一下,全部显示。 刚刚入门,这肯定不是最好的方法,感觉有好的建议,希望大神们提出,给予指导,拜谢。

2.导出word时,对所建的table合并单元格,该合并的单元格行列坐标为多少?(以至于怎么去赋值)

​ 细心的朋友,应该已经发现上面代码中有一个疑问。在我添加题量总和那一行时,是和上面一起遍历的。而我list【row-1】【0】= “题量总和”,list【row-1】【1】= “”,但是我的表格此之间已经合并了。 合并后的表格数据是怎么传进去的,是两个list【row-1】【0】+ list【row-1】【1】值拼接后传进去的。还是单独取的list【row-1】【0】中的值。毕竟中在row-1行每一列都赋值一遍,而在此之前前两列已经合并。 肯定有明白的大神。 像我一样的小白,可以没事的时候自己测试下。

五、参考

  1. http://53873039oycg.iteye.com/blog/2152009

  2. http://blog.csdn.net/zhouseawater/article/details/54289495