SpringBoot+Poi-tl根据Word模板动态生成word(含动态行表格、合并单元格)

本编文章继SpringBoot+Poi-tl根据Word模板动态生成word(含动态行表格)文章之后
介绍Poi-tl导出word的延伸功能:

所需依赖以及word模板所属位置 见 SpringBoot+Poi-tl根据Word模板动态生成word(含动态行表格),这里不再累赘。

直接介绍延伸功能的实现。

一、延伸功能

功能1:导出多个动态行表格(固定个数)
功能2:导出循环列表下的动态行表格(个数不固定)
功能3:导出合并单元格
功能4:导出循环列表下合并单元格
功能5:导出循环列表下合并单元格、外加一个动态行表格

二、功能实现

功能1:导出多个动态行表格(固定个数)

(1)新建一个word(orderD2.docx),编写word模板:

在这里插入图片描述

(2)在ExportWordController类中,编写相应的导出方法,供页面请求
	/**
	 * 销售订单信息导出word --- poi-tl(包含两个动态行表格)
	 * @throws IOException 
	 */
	@RequestMapping("/exportDataWordD4")
	public void exportDataWordD4(HttpServletRequest request,HttpServletResponse response) throws IOException{
		try {
			Map<String, Object> params = new HashMap<>();
			
			// TODO 渲染其他类型的数据请参考官方文档
			DecimalFormat df = new DecimalFormat("######0.00");   
			Calendar now = Calendar.getInstance(); 
			double money = 0;//总金额
			//组装表格列表数据
			List<Map<String,Object>> typeList=new ArrayList<Map<String,Object>>();
			for (int i = 0; i < 2; i++) {
				Map<String,Object> detailMap = new HashMap<String, Object>();
				detailMap.put("index", i+1);//序号
				if(i == 0){
					detailMap.put("sub_type", "监督技术装备");//商品所属大类名称
				}else if(i == 1){
					detailMap.put("sub_type", "火灾调查装备");//商品所属大类名称
				}else if(i == 2){
					detailMap.put("sub_type", "工程验收装备");//商品所属大类名称
				}
				
				double saleprice=Double.valueOf(String.valueOf(100+i));
				Integer buy_num=Integer.valueOf(String.valueOf(3+i));
				String buy_price=df.format(saleprice*buy_num);
				detailMap.put("buy_price", buy_price);//所属大类总价格
				money=money+Double.valueOf(buy_price);
				typeList.add(detailMap);
			}
			//组装表格列表数据
			List<Map<String,Object>> detailList=new ArrayList<Map<String,Object>>();
			for (int i = 0; i < 3; i++) {
				Map<String,Object> detailMap = new HashMap<String, Object>();
				detailMap.put("index", i+1);//序号
				if(i == 0 || i == 1){
					detailMap.put("product_type", "二级分类1");//商品二级分类
				}else{
					detailMap.put("product_type", "二级分类2");//商品二级分类
				}
				detailMap.put("title", "商品"+i);//商品名称
				detailMap.put("product_description", "套");//商品规格
				detailMap.put("buy_num", 3+i);//销售数量
				detailMap.put("saleprice", 100+i);//销售价格
				detailMap.put("technical_parameter", "技术参数"+i);//技术参数
				detailList.add(detailMap);
			}
			
			//总金额
			String order_money=String.valueOf(money);
			//金额中文大写
			String money_total = MoneyUtils.change(money);
			//word模板地址获取方式一:缺点---打jar包获取不到该路径
//			String basePath=ClassUtils.getDefaultClassLoader().getResource("").getPath()+"static/template/";
//			String resource =basePath+"orderD2.docx";//word模板地址
	        //word模板地址获取方式二:优点---相比上一种方式,这种方法不会在linux或者jar上失效
	      	ClassPathResource classPathResource = new ClassPathResource("static/template/orderD2.docx");
			String resource = classPathResource.getURL().getPath();
			//渲染表格  动态行
			HackLoopTableRenderPolicy  policy = new HackLoopTableRenderPolicy();
			Configure config = Configure.newBuilder()
					.bind("typeList", policy).bind("detailList", policy).build();
			
			XWPFTemplate template = XWPFTemplate.compile(resource, config).render(
					new HashMap<String, Object>() {{
						put("typeList", typeList);
						put("detailList",detailList);
						put("order_number", "2356346346645");
						put("y", now.get(Calendar.YEAR));//当前年
						put("m", (now.get(Calendar.MONTH) + 1));//当前月
						put("d", now.get(Calendar.DAY_OF_MONTH));//当前日
						put("order_money",order_money);//总金额
						put("money_total",money_total);//金额中文大写
					}}
			);
			//=================生成文件保存在本地D盘某目录下=================
			String temDir="D:/mimi/"+File.separator+"file/word/"; ;//生成临时文件存放地址
			//生成文件名
			Long time = new Date().getTime();
			// 生成的word格式
			String formatSuffix = ".docx";
			// 拼接后的文件名
			String fileName = time + formatSuffix;//文件名  带后缀
			
			FileOutputStream fos = new FileOutputStream(temDir+fileName);
			template.write(fos);
			//=================生成word到设置浏览默认下载地址=================
			// 设置强制下载不打开
			response.setContentType("application/force-download");
			// 设置文件名
			response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
			OutputStream out = response.getOutputStream();
			template.write(out);
			out.flush();
			out.close();
			template.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

}
(3)页面编写调用方法
<div class="m2" style="margin-top: 30px;">使用<span class="s1">POI-tl</span>根据word模板动态生成word(包含两个动态行表格)</div>
<a href="#" class="easyui-linkbutton" onclick="doExportWordD4();" data-options="iconCls:'icon-save'">导出word(包含两个动态行表格)</a>
//方式一导出word(包含两个动态行表格)
 function doExportWordD4(){
    window.location.href="<%=basePath%>/auth/exportWord/exportDataWordD4";
  }
(4)导出结果:

在这里插入图片描述

功能2:导出循环列表下的动态行表格(个数不固定)

如果列表的每一项不是简单的文本,而是包含很多文档内容,或者多级列表该怎么生成? 区块对的循环功能可以很好的循环列表,并且支持编号有序。

(1)新建一个word(order2.docx),编写word模板:

在这里插入图片描述

(2)在ExportWordController类中,编写相应的导出方法,供页面请求
/**
	 * 销售订单信息导出word --- poi-tl(包含动态行表格、循环列表中的动态行表格)
	 * @throws IOException 
	 */
	@RequestMapping("/exportDataWord4")
	public void exportDataWord4(HttpServletRequest request,HttpServletResponse response) throws IOException{
		try {
			Map<String, Object> params = new HashMap<>();
			
			// TODO 渲染其他类型的数据请参考官方文档
			DecimalFormat df = new DecimalFormat("######0.00");   
			Calendar now = Calendar.getInstance(); 
			double money = 0;//总金额
			//组装表格列表数据
			List<Map<String,Object>> typeList=new ArrayList<Map<String,Object>>();
			for (int i = 0; i < 2; i++) {
				Map<String,Object> detailMap = new HashMap<String, Object>();
				detailMap.put("index", i+1);//序号
				if(i == 0){
					detailMap.put("sub_type", "监督技术装备");//商品所属大类名称
				}else if(i == 1){
					detailMap.put("sub_type", "火灾调查装备");//商品所属大类名称
				}else if(i == 2){
					detailMap.put("sub_type", "工程验收装备");//商品所属大类名称
				}
				
				double saleprice=Double.valueOf(String.valueOf(100+i));
				Integer buy_num=Integer.valueOf(String.valueOf(3+i));
				String buy_price=df.format(saleprice*buy_num);
				detailMap.put("buy_price", buy_price);//所属大类总价格
				money=money+Double.valueOf(buy_price);
				typeList.add(detailMap);
			}
			//组装表格列表数据
			List<Map<String,Object>> detailList=new ArrayList<Map<String,Object>>();
			for (int i = 0; i < 3; i++) {
				Map<String,Object> detailMap = new HashMap<String, Object>();
				detailMap.put("index", i+1);//序号
				if(i == 0 || i == 1){
					detailMap.put("product_type", "二级分类1");//商品二级分类
				}else{
					detailMap.put("product_type", "二级分类2");//商品二级分类
				}
				detailMap.put("title", "商品"+i);//商品名称
				detailMap.put("product_description", "套");//商品规格
				detailMap.put("buy_num", 3+i);//销售数量
				detailMap.put("saleprice", 100+i);//销售价格
				detailMap.put("technical_parameter", "技术参数"+i);//技术参数
				detailList.add(detailMap);
			}
			
			
			List<Map<String,Object>> tList=new ArrayList<Map<String,Object>>();
			Map<String,Object> tMap = new HashMap<String, Object>();
			tMap.put("index", 1);
			tMap.put("sub_type", "监督技术装备");
			tMap.put("detailList", detailList);
			tMap.put("buy_price", 100);
			tList.add(tMap);
			
			tMap = new HashMap<String, Object>();
			tMap.put("index", 2);
			tMap.put("sub_type", "火灾调查装备");
			tMap.put("detailList", detailList);
			tMap.put("buy_price", 200);
			tList.add(tMap);
			
			tMap = new HashMap<String, Object>();
			tMap.put("index", 3);
			tMap.put("sub_type", "工程验收装备");
			tMap.put("detailList", detailList);
			tMap.put("buy_price", 300);
			tList.add(tMap);
			
			
			//总金额
			String order_money=String.valueOf(money);
			//金额中文大写
			String money_total = MoneyUtils.change(money);
			
			//word模板地址获取方式一:缺点---打jar包获取不到该路径
//			String basePath=ClassUtils.getDefaultClassLoader().getResource("").getPath()+"static/template/";
//			String resource =basePath+"order2.docx";//word模板地址
	        //word模板地址获取方式二:优点---相比上一种方式,这种方法不会在linux或者jar上失效
	      	ClassPathResource classPathResource = new ClassPathResource("static/template/order2.docx");
			String resource = classPathResource.getURL().getPath();

			//渲染表格  动态行
			HackLoopTableRenderPolicy  policy = new HackLoopTableRenderPolicy();
			Configure config = Configure.newBuilder()
					.bind("typeList", policy).bind("detailList", policy).build();
			
			XWPFTemplate template = XWPFTemplate.compile(resource, config).render(
					new HashMap<String, Object>() {{
						put("typeList", typeList);
						put("typeProducts",tList);
						put("order_number", "2356346346645");
						put("y", now.get(Calendar.YEAR));//当前年
						put("m", (now.get(Calendar.MONTH) + 1));//当前月
						put("d", now.get(Calendar.DAY_OF_MONTH));//当前日
						put("order_money",order_money);//总金额
						put("money_total",money_total);//金额中文大写
					}}
					);
			//=================生成文件保存在本地D盘某目录下=================
			String temDir="D:/mimi/"+File.separator+"file/word/"; ;//生成临时文件存放地址
			//生成文件名
			Long time = new Date().getTime();
			// 生成的word格式
			String formatSuffix = ".docx";
			// 拼接后的文件名
			String fileName = time + formatSuffix;//文件名  带后缀
			
			FileOutputStream fos = new FileOutputStream(temDir+fileName);
			template.write(fos);
			//=================生成word到设置浏览默认下载地址=================
			// 设置强制下载不打开
			response.setContentType("application/force-download");
			// 设置文件名
			response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
			OutputStream out = response.getOutputStream();
			template.write(out);
			out.flush();
			out.close();
			template.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
(3)页面编写调用方法
<div class="m2" style="margin-top: 30px;">使用<span class="s1">POI-tl</span>根据word模板动态生成word(包含动态行表格、循环列表下动态行表格)</div>
			<a href="#" class="easyui-linkbutton" onclick="doExportWord4();" data-options="iconCls:'icon-save'">导出word(包含动态行表格、循环列表下动态行表格)</a>
			
//方式一导出word(包含动态行表格、循环列表中的动态行表格)
  function doExportWord4(){
    window.location.href="<%=basePath%>/auth/exportWord/exportDataWord4";
  }
(4)导出结果:

在这里插入图片描述
在这里插入图片描述

功能3:导出合并单元格(一个列表下的合并行)

示例中:
比较复杂的表格,一个二级分类下,有多个商品。由7列组成,行数不定。

默认表格数据模型(MiniTableRenderData)实现了最基本的样式,当需求中的表格更加复杂的时候,我们完全可以设计好那些固定的部分,将需要动态渲染的部分单元格交给自定义模板渲染策略。

poi-tl提供了抽象表格策略DynamicTableRenderPolicy来实现这样的功能,{{detail_table}}标签可以在表格内的任意单元格内,DynamicTableRenderPolicy会获取XWPFTable对象进而获得操作整个表格的能力。

(1)新建一个word(order4.docx),编写word模板:

在这里插入图片描述

设置{{detail_table}}为自定义模板渲染策略(继承抽象表格策略DynamicTableRenderPolicy),自定义已有表格中部分单元格的渲染。

(2)代码实现:

新建渲染策略DetailTablePolicy2,继承于抽象表格策略:

package com.example.word.common.mergeCell2;

import java.util.List;
import java.util.Map;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import com.deepoove.poi.data.RowRenderData;
import com.deepoove.poi.policy.DynamicTableRenderPolicy;
import com.deepoove.poi.policy.MiniTableRenderPolicy;
import com.deepoove.poi.util.TableTools;

public class DetailTablePolicy2 extends DynamicTableRenderPolicy {

	// 填充数据所在行数
	int listsStartRow = 1;

	@Override
	public void render(XWPFTable table, Object data) {
		if (null == data) return;
		DetailData2 detailData = (DetailData2) data;

		// 商品订单详情列表数据 循环渲染
		List<RowRenderData> lists = detailData.getPlists();
		// 二级分类分组统计商品个数数据
		List<Map<String,Object>> tlists = detailData.getTlists();
		
		if (null != lists) {
			table.removeRow(listsStartRow);
			// 循环插入行
			for (int i = lists.size()-1; i >=0; i--) {
				XWPFTableRow insertNewTableRow = table.insertNewTableRow(listsStartRow);
				for (int j = 0; j < 7; j++)
					insertNewTableRow.createCell();
				// 渲染单行商品订单详情数据
				MiniTableRenderPolicy.Helper.renderRow(table, listsStartRow, lists.get(i));
			}
			//处理合并
			for (int i=0;i<lists.size();i++) {
				Object v =lists.get(i).getCells().get(1).getCellText();
				String type_name=String.valueOf(v);
				for(int j=0;j<tlists.size();j++){
					String typeName = String.valueOf(tlists.get(j).get("typeName"));
					Integer listSize = Integer.parseInt(String.valueOf(tlists.get(j).get("listSize")));
					if(type_name.equals(typeName)){
				        // 合并第1列的第i+1行到第i+listSize行的单元格
				        TableTools.mergeCellsVertically(table, 1, i+1, i+listSize);
				        //处理垂直居中
				        for (int y = 1; y < 7; y++){
					        XWPFTableCell cell = table.getRow(i+1).getCell(y);
					        cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); //垂直居中
					    }
				        tlists.remove(j);
				        break;
					}
				}
				System.out.println(v);
			}
		}
	}
}

DetailData2:

package com.example.word.common.mergeCell2;

import java.util.List;
import java.util.Map;

import com.deepoove.poi.data.RowRenderData;

public class DetailData2 {
	
    
    // 商品订单详情列表数据
    private List<RowRenderData> plists;
    
    // 二级分类分组统计商品个数数据
    private List<Map<String,Object>> tlists;
    

	public List<RowRenderData> getPlists() {
		return plists;
	}

	public void setPlists(List<RowRenderData> plists) {
		this.plists = plists;
	}

	public List<Map<String,Object>> getTlists() {
		return tlists;
	}

	public void setTlists(List<Map<String,Object>> tlists) {
		this.tlists = tlists;
	}


	
}

PaymentData2:

package com.example.word.common.mergeCell2;

import com.deepoove.poi.el.Name;

public class PaymentData2 {
	
    @Name("detail_table")
    private DetailData2 detailTable;
    private String total;
    public void setDetailTable(DetailData2 detailTable) {
        this.detailTable = detailTable;
    }
    public DetailData2 getDetailTable() {
        return this.detailTable;
    }
    public String getTotal() {
		return total;
	}

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

}

在ExportWordController类中,编写相应的导出方法,供页面请求:

/**
	 * 销售订单信息导出word --- poi-tl(合并单元格(一个列表下的合并行)--商品订单明细)
	 * @throws IOException 
	 */
	@RequestMapping("/exportDataWord6")
	public void exportDataWord6(HttpServletRequest request,HttpServletResponse response) throws IOException{
		try {
			Map<String, Object> params = new HashMap<>();
			
			// TODO 渲染其他类型的数据请参考官方文档
			
			//word模板地址获取方式一:缺点---打jar包获取不到该路径
//			String basePath=ClassUtils.getDefaultClassLoader().getResource("").getPath()+"static/template/";
//			String resource =basePath+"order4.docx";//word模板地址
	        //word模板地址获取方式二:优点---相比上一种方式,这种方法不会在linux或者jar上失效
	      	ClassPathResource classPathResource = new ClassPathResource("static/template/order4.docx");
			String resource = classPathResource.getURL().getPath();
			
			PaymentData2 datas = new PaymentData2();
			TableStyle rowStyle = new TableStyle();
			rowStyle = new TableStyle();
			rowStyle.setAlign(STJc.CENTER);
			DetailData2 detailTable = new DetailData2();
			List<RowRenderData> plists =new ArrayList<RowRenderData>();
		    for(int i=0;i<6;i++){
		    	String typeName="二级分类1";
		    	if(i == 3 || i == 4){
		    		typeName="二级分类2";
		    	}else if(i == 5){
		    		typeName="二级分类3";
		    	}
		    	String index = String.valueOf(i+1);
		    	RowRenderData plist = RowRenderData.build(index, typeName, "商品"+i, "套", "2","100","技术参数"+i);
		    	plist.setRowStyle(rowStyle);
				plists.add(plist);
				
		    }
		    //二级分类 分组统计   商品个数
		    List<Map<String,Object>> tlists = new ArrayList<Map<String,Object>>();
		    Map<String,Object> map = new HashMap<String, Object>();
		    map.put("typeName", "二级分类1");
		    map.put("listSize", "3");
		    tlists.add(map);
		    map = new HashMap<String, Object>();
		    map.put("typeName", "二级分类2");
		    map.put("listSize", "2");
		    tlists.add(map);
		    map = new HashMap<String, Object>();
		    map.put("typeName", "二级分类3");
		    map.put("listSize", "1");
		    tlists.add(map);
		    
		    
			detailTable.setPlists(plists);
			detailTable.setTlists(tlists);
			datas.setDetailTable(detailTable);
			datas.setTotal("1000");
			
			Configure config = Configure.newBuilder().bind("detail_table", new DetailTablePolicy2()).build();
			
			XWPFTemplate template = XWPFTemplate.compile(resource, config).render(datas);
			
			//=================生成文件保存在本地D盘某目录下=================
			String temDir="D:/mimi/"+File.separator+"file/word/"; ;//生成临时文件存放地址
			//生成文件名
			Long time = new Date().getTime();
			// 生成的word格式
			String formatSuffix = ".docx";
			// 拼接后的文件名
			String fileName = time + formatSuffix;//文件名  带后缀
			
			FileOutputStream fos = new FileOutputStream(temDir+fileName);
			template.write(fos);
			//=================生成word到设置浏览默认下载地址=================
			// 设置强制下载不打开
			response.setContentType("application/force-download");
			// 设置文件名
			response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
			OutputStream out = response.getOutputStream();
			template.write(out);
			out.flush();
			out.close();
			template.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}

注意: @Name(“detail_table”) 是必须的,不然word表格无法渲染, name可以自定义,要和 模板的一致
在这里插入图片描述

(3)页面编写调用方法
<div class="m2" style="margin-top: 30px;">使用<span class="s1">POI-tl</span>根据word模板动态生成word(合并单元格2)</div>
<a href="#" class="easyui-linkbutton" onclick="doExportWord6();" data-options="iconCls:'icon-save'">导出word(合并单元格(一个列表下的合并行)--商品订单明细)</a>
<div style="font-size: 13px;color:#cccccc">如:合并第1列的第1行到第3行的单元格</div>
			
//方式一导出word(合并单元格(一个列表下的合并行)--商品订单明细))
 function doExportWord6(){
   window.location.href="<%=basePath%>/auth/exportWord/exportDataWord6";
 }
(4)导出结果:

在这里插入图片描述

功能4:导出循环列表下合并单元格(循环列表下的合并行)

在功能3的基础上做修改:

(1)新建一个word(order5.docx),编写word模板:

在这里插入图片描述

(2)代码实现:

复用 功能3中的合并处理类,修改PaymentData2.java类,添加 typeLists变量存储循环列表(@Name(“typeLists”)用于表格渲染,不能省略):

package com.example.word.common.mergeCell2;


import java.util.List;
import java.util.Map;

import com.deepoove.poi.el.Name;

public class PaymentData2 {
	
    @Name("detail_table")
    private DetailData2 detailTable;
    @Name("typeLists")
    private List<Map<String,Object>> typeLists;//所属大类统计列表
    private String total;
    public void setDetailTable(DetailData2 detailTable) {
        this.detailTable = detailTable;
    }

    public DetailData2 getDetailTable() {
        return this.detailTable;
    }

	public String getTotal() {
		return total;
	}

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

	public List<Map<String, Object>> getTypeLists() {
		return typeLists;
	}

	public void setTypeLists(List<Map<String, Object>> typeLists) {
		this.typeLists = typeLists;
	}
	
}

在ExportWordController类中,编写相应的导出方法,供页面请求:

	
	/**
	 * 销售订单信息导出word --- poi-tl(合并单元格(循环列表下的合并行)--商品订单明细)
	 * @throws IOException 
	 */
	@RequestMapping("/exportDataWord7")
	public void exportDataWord7(HttpServletRequest request,HttpServletResponse response) throws IOException{
		try {
			Map<String, Object> params = new HashMap<>();
			
			// TODO 渲染其他类型的数据请参考官方文档
			//word模板地址获取方式一:缺点---打jar包获取不到该路径
//			String basePath=ClassUtils.getDefaultClassLoader().getResource("").getPath()+"static/template/";
//			String resource =basePath+"order5.docx";//word模板地址
	        //word模板地址获取方式二:优点---相比上一种方式,这种方法不会在linux或者jar上失效
	      	ClassPathResource classPathResource = new ClassPathResource("static/template/order5.docx");
			String resource = classPathResource.getURL().getPath();
			
			PaymentData2 datas = new PaymentData2();
			TableStyle rowStyle = new TableStyle();
			rowStyle = new TableStyle();
			rowStyle.setAlign(STJc.CENTER);
			//组装循环体
			List<Map<String,Object>> typeLists = new ArrayList<Map<String,Object>>();
			for(int x=0;x<3;x++){
				DetailData2 detailTable = new DetailData2();
				List<RowRenderData> plists =new ArrayList<RowRenderData>();
				for(int i=0;i<6;i++){
					String typeName="二级分类1"+x;
					if(i == 3 || i == 4){
						typeName="二级分类2"+x;
					}else if(i == 5){
						typeName="二级分类3"+x;
					}
					String index = String.valueOf(i+1);
					RowRenderData plist = RowRenderData.build(index, typeName, "商品"+i, "套", "2","100","技术参数"+i);
					plist.setRowStyle(rowStyle);
					plists.add(plist);
					
				}
				//二级分类 分组统计   商品个数
				List<Map<String,Object>> tlists = new ArrayList<Map<String,Object>>();
				Map<String,Object> map = new HashMap<String, Object>();
				map.put("typeName", "二级分类1"+x);
				map.put("listSize", "3");
				tlists.add(map);
				map = new HashMap<String, Object>();
				map.put("typeName", "二级分类2"+x);
				map.put("listSize", "2");
				tlists.add(map);
				map = new HashMap<String, Object>();
				map.put("typeName", "二级分类3"+x);
				map.put("listSize", "1");
				tlists.add(map);
				
				
				detailTable.setPlists(plists);
				detailTable.setTlists(tlists);
				Map<String,Object> data= new HashMap<String, Object>();
				data.put("detail_table", detailTable);
				data.put("sub_type", "大类"+x);
				data.put("total_price", 100+x);
				typeLists.add(data);
			}
			datas.setTypeLists(typeLists);
			
			Configure config = Configure.newBuilder().bind("detail_table", new DetailTablePolicy2()).build();
			
			XWPFTemplate template = XWPFTemplate.compile(resource, config).render(datas);
			
			//=================生成文件保存在本地D盘某目录下=================
			String temDir="D:/mimi/"+File.separator+"file/word/"; ;//生成临时文件存放地址
			//生成文件名
			Long time = new Date().getTime();
			// 生成的word格式
			String formatSuffix = ".docx";
			// 拼接后的文件名
			String fileName = time + formatSuffix;//文件名  带后缀
			
			FileOutputStream fos = new FileOutputStream(temDir+fileName);
			template.write(fos);
			//=================生成word到设置浏览默认下载地址=================
			// 设置强制下载不打开
			response.setContentType("application/force-download");
			// 设置文件名
			response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
			OutputStream out = response.getOutputStream();
			template.write(out);
			out.flush();
			out.close();
			template.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
(3)页面编写调用方法
<div class="m2" style="margin-top: 30px;">使用<span class="s1">POI-tl</span>根据word模板动态生成word(循环列表下的动态行表格以及合并单元格)</div>
<a href="#" class="easyui-linkbutton" onclick="doExportWord7();" data-options="iconCls:'icon-save'">导出word(合并单元格(循环列表下的合并行)--商品订单明细)</a>
        
			
 //方式一导出word(合并单元格(循环列表下的合并行)--商品订单明细)
 function doExportWord7(){
   window.location.href="<%=basePath%>/auth/exportWord/exportDataWord7";
 }
(4)导出结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

功能5:导出循环列表下合并单元格、外加一个动态行表格

(1)新建一个word(order6.docx),编写word模板:

在这里插入图片描述
在这里插入图片描述

(2)代码实现:

功能4中已经编写了循环列表合并处理,见上述。 讲解下如何在循环列表合并的基础上,再加上一个动态行表格:

①新增DetailTablePolicy4用于 另加的动态行表格的插入与渲染:

package com.example.word.common.mergeCell3;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
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.STJc;

import com.deepoove.poi.data.RowRenderData;
import com.deepoove.poi.data.style.TableStyle;
import com.deepoove.poi.policy.DynamicTableRenderPolicy;
import com.deepoove.poi.policy.MiniTableRenderPolicy;
import com.deepoove.poi.util.TableTools;

/**
 * 商品所属大类统计 表格动态行插入、渲染、合并单元格处理
 * @author Administrator
 *
 */
public class DetailTablePolicy4 extends DynamicTableRenderPolicy {

	// 填充数据所在行数
	int listsStartRow = 1;

	@Override
	public void render(XWPFTable table, Object data) {
		if (null == data) return;
		//文本居中
		TableStyle rowStyle = new TableStyle();
		rowStyle = new TableStyle();
		rowStyle.setAlign(STJc.CENTER);
		
		List<RowRenderData> lists = new ArrayList<RowRenderData>();
		//所属大类列表数据
		List<Map<String,Object>> typeLists = (List<Map<String, Object>>) data;
        for(Map<String,Object> map:typeLists){
        	String index =String.valueOf(map.get("index")); 
        	String sub_type =String.valueOf(map.get("sub_type")); 
        	String total_price =String.valueOf(map.get("total_price")); 
        	RowRenderData tlist = RowRenderData.build(index, sub_type, total_price,total_price);
        	tlist.setRowStyle(rowStyle);
			lists.add(tlist);
        }
		
		if (null != lists) {
			table.removeRow(listsStartRow);
			// 循环插入行
			for (int i = lists.size()-1; i >=0; i--) {
				XWPFTableRow insertNewTableRow = table.insertNewTableRow(listsStartRow);
				for (int j = 0; j < 4; j++)
					insertNewTableRow.createCell();
				// 渲染单行所属大类数据
				MiniTableRenderPolicy.Helper.renderRow(table, listsStartRow, lists.get(i));
			}
		}
	}
}

②PaymentData2类新增两个变量:

    private String order_money;//订单总金额
    private String money_total;//订单总金额的中文大写

③导出方法修改
在功能4的导出方法做如下修改:
在这里插入图片描述

导出方法完整代码:

	
	/**
	 * 销售订单信息导出word --- poi-tl(合并单元格(循环列表下的合并行)--商品订单明细、另加一个动态行表格)
	 * @throws IOException 
	 */
	@RequestMapping("/exportDataWord8")
	public void exportDataWord8(HttpServletRequest request,HttpServletResponse response) throws IOException{
		try {
			Map<String, Object> params = new HashMap<>();
			
			// TODO 渲染其他类型的数据请参考官方文档
			//word模板地址获取方式一:缺点---打jar包获取不到该路径
//			String basePath=ClassUtils.getDefaultClassLoader().getResource("").getPath()+"static/template/";
//			String resource =basePath+"order6.docx";//word模板地址
	        //word模板地址获取方式二:优点---相比上一种方式,这种方法不会在linux或者jar上失效
	      	ClassPathResource classPathResource = new ClassPathResource("static/template/order6.docx");
			String resource = classPathResource.getURL().getPath();
			
			PaymentData2 datas = new PaymentData2();
			TableStyle rowStyle = new TableStyle();
			rowStyle = new TableStyle();
			rowStyle.setAlign(STJc.CENTER);
			//组装循环体
			List<Map<String,Object>> typeLists = new ArrayList<Map<String,Object>>();
			List<Map<String,Object>> typeTotalList = new ArrayList<Map<String,Object>>();
			for(int x=0;x<3;x++){
				DetailData2 detailTable = new DetailData2();
				List<RowRenderData> plists =new ArrayList<RowRenderData>();
				for(int i=0;i<6;i++){
					String typeName="二级分类1"+x;
					if(i == 3 || i == 4){
						typeName="二级分类2"+x;
					}else if(i == 5){
						typeName="二级分类3"+x;
					}
					String index = String.valueOf(i+1);
					RowRenderData plist = RowRenderData.build(index, typeName, "商品"+i, "套", "2","100","技术参数"+i);
					plist.setRowStyle(rowStyle);
					plists.add(plist);
					
				}
				//二级分类 分组统计   商品个数
				List<Map<String,Object>> tlists = new ArrayList<Map<String,Object>>();
				Map<String,Object> map = new HashMap<String, Object>();
				map.put("typeName", "二级分类1"+x);
				map.put("listSize", "3");
				tlists.add(map);
				map = new HashMap<String, Object>();
				map.put("typeName", "二级分类2"+x);
				map.put("listSize", "2");
				tlists.add(map);
				map = new HashMap<String, Object>();
				map.put("typeName", "二级分类3"+x);
				map.put("listSize", "1");
				tlists.add(map);
				
				
				detailTable.setPlists(plists);
				detailTable.setTlists(tlists);
				Map<String,Object> data= new HashMap<String, Object>();
				data.put("detail_table", detailTable);
				data.put("index", x+1);
				data.put("sub_type", "大类"+x);
				data.put("total_price", 100+x);
				typeLists.add(data);
			}
			datas.setTypeLists(typeLists);
			datas.setOrder_money("100");
			datas.setMoney_total("壹佰元整");
			
			Configure config = Configure.newBuilder().bind("detail_table", new DetailTablePolicy2())
					.bind("typeLists", new DetailTablePolicy4()).build();
			
			XWPFTemplate template = XWPFTemplate.compile(resource, config).render(datas);
			
			//=================生成文件保存在本地D盘某目录下=================
			String temDir="D:/mimi/"+File.separator+"file/word/"; ;//生成临时文件存放地址
			//生成文件名
			Long time = new Date().getTime();
			// 生成的word格式
			String formatSuffix = ".docx";
			// 拼接后的文件名
			String fileName = time + formatSuffix;//文件名  带后缀
			
			FileOutputStream fos = new FileOutputStream(temDir+fileName);
			template.write(fos);
			//=================生成word到设置浏览默认下载地址=================
			// 设置强制下载不打开
			response.setContentType("application/force-download");
			// 设置文件名
			response.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
			OutputStream out = response.getOutputStream();
			template.write(out);
			out.flush();
			out.close();
			template.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
(3)页面编写调用方法
<div class="m2" style="margin-top: 30px;">使用<span class="s1">POI-tl</span>根据word模板动态生成word(循环列表下的动态行表格以及合并单元格)</div>
<a href="#" class="easyui-linkbutton" onclick="doExportWord8();" data-options="iconCls:'icon-save'">导出word(合并单元格(循环列表下的合并行)--商品订单明细、另加一个动态行表格)</a>		
//方式一导出word(合并单元格(循环列表下的合并行)--商品订单明细、另加一个动态行表格)
function doExportWord8(){
  window.location.href="<%=basePath%>/auth/exportWord/exportDataWord8";
}
(4)导出结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三、完整代码

点击此处获取

  • 25
    点赞
  • 137
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
在Spring Boot中使用poi-tl库来导出带有合并列的Word表格并下载,您可以按照以下步骤操作: 1. 首先,确保您的Spring Boot项目中已经添加poi-tl的依赖。您可以在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.6.0</version> </dependency> ``` 2. 创建一个Controller来处理导出请求。例如,创建一个名为WordExportController的类,并添加一个处理导出请求的方法。 ```java import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.data.*; import com.deepoove.poi.util.BytePictureUtils; import org.apache.poi.xwpf.usermodel.XWPFTable; import org.apache.poi.xwpf.usermodel.XWPFTableRow; import org.springframework.core.io.InputStreamResource; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; @Controller public class WordExportController { @GetMapping("/export") public ResponseEntity<InputStreamResource> exportWord() throws IOException { // 创建一个数据模型 List<List<String>> tableData = new ArrayList<>(); tableData.add(createRow("Merged Cells", "Cell 3")); tableData.add(createRow("Cell 4", "Cell 6")); // 使用poi-tl的XWPFTemplate来生成Word文档 XWPFTemplate template = XWPFTemplate.compile("templates/template.docx").render( new DataTable(tableData) .setHeader(createRow("Header 1", "Header 2")) .setCellWidth(2000) // 设置单元格宽度 .setHeaderCellStyle(new CellStyle().setBold(true).setColor("FFFFFF").setBgColor("336699")) .setOddRowCellStyle(new CellStyle().setColor("FFFFFF").setBgColor("99CCFF")) .setEvenRowCellStyle(new CellStyle().setColor("FFFFFF").setBgColor("CCEEFF")) ); // 将生成Word文档转换为字节数组 ByteArrayOutputStream out = new ByteArrayOutputStream(); template.write(out); byte[] documentBytes = out.toByteArray(); // 设置下载响应的头信息 HttpHeaders headers = new HttpHeaders(); headers.setContentDispositionFormData("attachment", "merged_table.docx"); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); // 创建一个包Word文档字节数组的InputStreamResource InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(documentBytes)); // 返回响应实体 return ResponseEntity.ok() .headers(headers) .body(resource); } private List<String> createRow(String cell1, String cell2) { List<String> row = new ArrayList<>(); row.add(cell1); row.add(cell2); return row; } } ``` 3. 在resources目录下创建一个名为template.docx的Word模板文件。在模板文件中,您可以根据自己的需求设置表格样式和内容。 4. 启动您的Spring Boot应用程序,并访问导出请求的URL(例如:http://localhost:8080/export)。将会自动下载名为merged_table.docx的Word文档,其中包合并列的表格。 请确保按照您的需求修改代码,并根据模板文件的位置进相应的调整。 希望对您有所帮助!如果您有任何其他问题,请随时提问。
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值