直接上代码,原理之前的随笔已经讲过了。http://www.cnblogs.com/hdwang/p/7115835.html

1.先看看效果

2.html代码,含js代码

2.1 common.js

/**
* Created by hdwang on 2017/6/23.
*/
var language = { "search": "", "sSearch" : "搜索", "sUrl" : "", "sProcessing" : "正在加载数据...", "sLengthMenu" : "显示_MENU_条 ", "sZeroRecords" : "没有您要搜索的内容",
"sInfo" : "从_START_ 到 _END_ 条记录——总记录数为 _TOTAL_ 条", "sInfoEmpty" : "记录数为0", "sInfoFiltered" : "(全部记录数 _MAX_ 条)", "sInfoPostFix" : "",
"oPaginate": { "sFirst" : "第一页", "sPrevious" : " 上一页 ", "sNext" : " 下一页 ", "sLast" : " 最后一页 " }
}; /**
* 将参数对象转换成url查询参数
* @param params 参数对象
* @returns {string} url查询参数
*/
function getUrlParams(params) {
var queryStr = '';
var isFirstParam = true;
for(var key in params){
if(isFirstParam){
queryStr += key + '=' + params[key];
isFirstParam = false;
}else{
queryStr += '&' + key + '=' + params[key];
}
}
return queryStr;
}

2.2 home.ftl

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
<title>cpm system</title> <!-- Bootstrap -->
<link href="/thirdlib/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <!-- datatables -->
<link href="/thirdlib/datatables/css/jquery.dataTables.min.css" rel="stylesheet"/> <link href="/css/common.css" rel="stylesheet" />
</head>
<body> <div id="tableArea" style="padding: 100px;">
<div>
<a href="/home/export">导出</a>
</div>
    <table id="rowspanTable" class="table table-bordered">
<thead>
<th>地区</th>
<th>公司</th>
<th>部门</th>
<th>员工姓名</th>
</thead>
</table> </div> </body> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="/thirdlib/jquery/jquery-2.0.3.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="/thirdlib/bootstrap/js/bootstrap.min.js"></script> <!-- datatables -->
<script src="/thirdlib/datatables/js/jquery.dataTables.min.js"></script> <script src="/js/common.js"></script> <script type="text/javascript">
$(function(){ $('#rowspanTable').dataTable( {
"paging": true,
"processing": true,
"serverSide": true,
"searching":false, //搜索栏
"lengthChange" : false, //是否允许改变每页显示的数据条数
"pageLength": 10, //每行显示记录数
"info":true, //开启Datatables信息显示(记录数等)
"ordering":false, //全局定义是否启用排序,优先级比columns.orderable高
"language": language,
"ajax": {
"url": "/home/query",
"type": "POST"
},
"columns": [
{"data":"area", "orderable": false,"searchable": false},
{ "data": "company", "orderable": false ,"searchable": false},
{ "data": "department", "orderable": false,"searchable": false },
{ "data": "userName", "orderable": false ,"searchable": false}
],
"columnDefs": [{
targets: [0,1,2], //第1,2,3列
createdCell: function (td, cellData, rowData, row, col) {
var rowspan = 1;
if(col == 0){
rowspan = rowData.areaRowSpan;
}
if(col ==1){
rowspan = rowData.companyRowSpan;
}
if(col ==2){
rowspan = rowData.departmentRowSpan;
} if (rowspan > 1) {
$(td).attr('rowspan', rowspan)
}
if (rowspan == 0) {
$(td).remove();
}
}
}]
} ); }); </script> </html>

3.后台代码

3.1 分页参数对象

package com.xincheng.cpm.common;

/**
* Created by hdwang on 2017/6/22.
* 分页参数
*/
public class PageParam { /**
* 第几次绘画(前端标识)
*/
private int draw; /**
* 起始记录(从0开始),mysql也是从0开始,吻合,good!
*/
private int start; /**
* 页大小
*/
private int length; public int getDraw() {
return draw;
} public void setDraw(int draw) {
this.draw = draw;
} public int getStart() {
return start;
} public void setStart(int start) {
this.start = start;
} public int getLength() {
return length;
} public void setLength(int length) {
this.length = length;
} /**
* 第几页(0-n)
*/
public int getPage(){
return this.start/this.length;
}
}

3.2 数据返回对象

package com.xincheng.cpm.common;

import java.util.List;

/**
* Created by hdwang on 2017/6/22.
* 表格数据(datatables)
*/
public class TableData<T> { /**
* 第几次绘画(前端标识)
*/
private int draw; /**
* 行过滤(不知道干嘛的)
*/
private int recordsFiltered; /**
* 总行数
*/
private int recordsTotal; /**
* 行数据
*/
private List<T> data; /**
* 起始记录(用于前端初始化序列号用的)
*/
private int start; /**
* 错误信息
*/
private String error; public int getDraw() {
return draw;
} public void setDraw(int draw) {
this.draw = draw;
} public int getRecordsFiltered() {
return recordsFiltered;
} public void setRecordsFiltered(int recordsFiltered) {
this.recordsFiltered = recordsFiltered;
} public int getRecordsTotal() {
return recordsTotal;
} public void setRecordsTotal(int recordsTotal) {
this.recordsTotal = recordsTotal;
} public List<T> getData() {
return data;
} public void setData(List<T> data) {
this.data = data;
} public int getStart() {
return start;
} public void setStart(int start) {
this.start = start;
} public String getError() {
return error;
} public void setError(String error) {
this.error = error;
}
}

3.3 数据实体对象

package com.xincheng.cpm.common;

import java.io.Serializable;

/**
* Created by hdwang on 2017/7/14.
*/
public class Member{ private String area;
private String company;
private String department;
private String userName; private Integer areaRowSpan;
private Integer companyRowSpan;
private Integer departmentRowSpan; public Member(String area,String company,String department,String userName){
this.area = area;
this.company = company;
this.department = department;
this.userName = userName;
} public String getArea() {
return area;
} public void setArea(String area) {
this.area = area;
} public String getCompany() {
return company;
} public void setCompany(String company) {
this.company = company;
} public String getDepartment() {
return department;
} public void setDepartment(String department) {
this.department = department;
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} public Integer getAreaRowSpan() {
return areaRowSpan;
} public void setAreaRowSpan(Integer areaRowSpan) {
this.areaRowSpan = areaRowSpan;
} public Integer getCompanyRowSpan() {
return companyRowSpan;
} public void setCompanyRowSpan(Integer companyRowSpan) {
this.companyRowSpan = companyRowSpan;
} public Integer getDepartmentRowSpan() {
return departmentRowSpan;
} public void setDepartmentRowSpan(Integer departmentRowSpan) {
this.departmentRowSpan = departmentRowSpan;
}
}

3.4 导出相关类

package com.xincheng.cpm.common;

/**
* Created by hdwang on 2017/7/14.
*/
public class ExcelData {
private String value;//单元格的值
private int colSpan = 1;//单元格跨几列
private int rowSpan = 1;//单元格跨几行
private boolean alignCenter;//单元格是否居中,默认不居中,如果选择是,则水平和上下都居中
public boolean isAlignCenter() {
return alignCenter;
}
public void setAlignCenter(boolean alignCenter) {
this.alignCenter = alignCenter;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public int getColSpan() {
return colSpan;
}
public void setColSpan(int colSpan) {
this.colSpan = colSpan;
}
public int getRowSpan() {
return rowSpan;
}
public void setRowSpan(int rowSpan) {
this.rowSpan = rowSpan;
}
}
package com.xincheng.cpm.common;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.util.List; /**
* Created by hdwang on 2017/7/14.
*/
public class ExcelUtil { /**
* 生成excel工作簿
* @param sheetTitle sheet名称
* @param titles 标题
* @param rows 行数据
* @return 工作簿
*/
public XSSFWorkbook execute(String sheetTitle,String[] titles,List<List<ExcelData>> rows) {
//定义工作簿
XSSFWorkbook workbook = new XSSFWorkbook(); //th样式
CellStyle titleStyle = workbook.createCellStyle();
titleStyle.setBorderBottom((short) 1);
titleStyle.setBorderRight((short)1);
titleStyle.setBorderLeft((short)1);
titleStyle.setBorderTop((short)1);
titleStyle.setVerticalAlignment((short)1);
titleStyle.setAlignment((short)2);
XSSFFont font = workbook.createFont();
font.setBold(true);
titleStyle.setFont(font);
//td样式
CellStyle style = workbook.createCellStyle();
style.setBorderBottom((short)1);
style.setBorderRight((short)1);
style.setBorderLeft((short)1);
style.setBorderTop((short)1);
style.setVerticalAlignment((short)1); //创建工作表
XSSFSheet sheet = workbook.createSheet(sheetTitle);
sheet.setDefaultRowHeightInPoints(20.0F); //创建标题行
XSSFRow titleRow = sheet.createRow(0); for(int col=0;col<titles.length;col++) { //遍历列
Cell cell = titleRow.createCell(col);
cell.setCellStyle(titleStyle);
cell.setCellValue(titles[col]); for(int row=0;row<rows.size();row++){ //遍历行
int rowIndex = row+1;
XSSFRow contentRow = sheet.getRow(rowIndex);
if(contentRow == null){
contentRow = sheet.createRow(rowIndex);
}
ExcelData data = rows.get(row).get(col);
Cell contentRowCell = contentRow.createCell(col);
contentRowCell.setCellStyle(style);
contentRowCell.setCellValue(data.getValue());
//合并单元格
if (data.getColSpan() > 1 || data.getRowSpan() > 1) {
CellRangeAddress cra = new CellRangeAddress(rowIndex, rowIndex + data.getRowSpan() - 1, col, col + data.getColSpan() - 1);
sheet.addMergedRegion(cra);
}
}
} return workbook;
}
}

3.5 controller层

package com.xincheng.cpm.controller;

import com.chenrd.common.excel.ExportExcel;
import com.xincheng.cpm.common.*;
import com.xincheng.cpm.entity.cpm.User;
import com.xincheng.cpm.service.UserService;
import com.xincheng.cpm.vo.IncomeDailyVO;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.*; /**
* Created by hdwang on 2017/6/19.
*/
@Controller
@RequestMapping("/home")
public class HomeController { @Autowired
UserService userService; @RequestMapping("")
public String index(HttpSession session, ModelMap map, HttpServletRequest request){
User user = (User) session.getAttribute("user");
map.put("user",user);
return "home";
} @RequestMapping(value="/query",method= RequestMethod.POST)
@ResponseBody
public TableData<Member> getUserByPage(PageParam pageParam, User user){
Page<Member> userPage = this.getMembers(pageParam);
TableData<Member> datas = new TableData<>();
datas.setDraw(pageParam.getDraw());
datas.setStart(pageParam.getStart());
datas.setData(userPage.getContent());
datas.setRecordsFiltered((int)userPage.getTotalElements());
datas.setRecordsTotal((int)userPage.getTotalElements());
return datas;
} private Page<Member> getMembers(PageParam pageParam) {
//1.模拟数据库查询
Pageable pageable = new PageRequest(pageParam.getPage(), pageParam.getLength());
long count = 6;
List<Member> members = getMembersFromDb(); //2.计算rowspan
this.countRowspan(members); Page<Member> memberPage = new PageImpl<Member>(members,pageable,count);
return memberPage;
} private void countRowspan(List<Member> members) {
Map<String,Integer> propertyCountMap = this.countPropertyCount(members);
List<String> hadGetKeys = new ArrayList<>(); //曾经取过的key
for(Member member:members){
String areaKey = member.getArea();
String companyKey = areaKey+member.getCompany();
String departmentKey = companyKey+ member.getDepartment(); Integer areaCount = propertyCountMap.get(areaKey);
if(areaCount == null){
member.setAreaRowSpan(1);
}else{
if(hadGetKeys.contains(areaKey)){
member.setAreaRowSpan(0); //曾经取过
}else{
member.setAreaRowSpan(areaCount); //第一次取
hadGetKeys.add(areaKey);
}
} Integer companyCount = propertyCountMap.get(companyKey);
if(companyCount == null){
member.setCompanyRowSpan(1);
}else {
if(hadGetKeys.contains(companyKey)){
member.setCompanyRowSpan(0);
}else{
member.setCompanyRowSpan(companyCount);
hadGetKeys.add(companyKey);
}
} Integer departmentCount = propertyCountMap.get(departmentKey);
if(companyCount == null){
member.setDepartmentRowSpan(1);
}else {
if(hadGetKeys.contains(departmentKey)){
member.setDepartmentRowSpan(0);
}else{
member.setDepartmentRowSpan(departmentCount);
hadGetKeys.add(departmentKey);
}
}
}
} private List<Member> getMembersFromDb() {
Member member1 = new Member("安徽","A","人力资源部"," 小红");
Member member2 = new Member("安徽","B","人力资源部"," 小明");
Member member3 = new Member("浙江","C","人力资源部"," 小君");
Member member4 = new Member("浙江","C","技术部"," 小王");
Member member5 = new Member("浙江","D","技术部"," 小李");
Member member6 = new Member("浙江","D","人力资源部"," 小刚");
List<Member> members = new ArrayList<>();
members.add(member1);
members.add(member2);
members.add(member3);
members.add(member4);
members.add(member5);
members.add(member6);
return members;
} /**
* 统计每个字段的每组成员个数
* @param rows 记录
* @return 每个字段的每组成员个数
*/
private Map<String,Integer> countPropertyCount(List<Member> rows){ Map<String,Integer> propertyCountMap = new HashMap<>(); for(Member member:rows){
// "area": 无父级分组
String area = member.getArea();
if(propertyCountMap.get(area) == null){
propertyCountMap.put(area,1);
}else{
int count = propertyCountMap.get(area);
propertyCountMap.put(area,count+1);
} // "company":有area父组
String company = member.getCompany();
String uniqueParent = member.getArea();
String key = uniqueParent + company;
if(propertyCountMap.get(key) == null){
propertyCountMap.put(key,1);
}else{
int count = propertyCountMap.get(key);
propertyCountMap.put(key,count+1);
} // "department": 有area,company这两个父组
String department = member.getDepartment();
uniqueParent = member.getArea()+member.getCompany();
key = uniqueParent + department;
if(propertyCountMap.get(key) == null){
propertyCountMap.put(key,1);
}else{
int count = propertyCountMap.get(key);
propertyCountMap.put(key,count+1);
}
} return propertyCountMap;
} @RequestMapping("/export")
public void export(HttpServletResponse response) throws IOException {
List<Member> members = this.getMembersFromDb();
this.countRowspan(members); List<List<ExcelData>> rows = new ArrayList<>();
for(Member member:members){
List<ExcelData> row = new ArrayList<>();
ExcelData col1 = new ExcelData();
col1.setValue(member.getArea());
col1.setRowSpan(member.getAreaRowSpan());
row.add(col1); ExcelData col2 = new ExcelData();
col2.setValue(member.getCompany());
col2.setRowSpan(member.getCompanyRowSpan());
row.add(col2); ExcelData col3 = new ExcelData();
col3.setValue(member.getDepartment());
col3.setRowSpan(member.getDepartmentRowSpan());
row.add(col3); ExcelData col4 = new ExcelData();
col4.setValue(member.getUserName());
row.add(col4); rows.add(row);
} OutputStream outputStream = response.getOutputStream();
try {
String filename = URLEncoder.encode("员工" + ".xlsx", "UTF-8");
response.setContentType("application/vnd.ms-excel");
response.addHeader("Content-Disposition", "octet-stream;filename=" + filename); ExcelUtil excelUtil = new ExcelUtil();
XSSFWorkbook workbook = excelUtil.execute("sheet1",new String[]{"地区","公司","部门","员工姓名"},rows);
workbook.write(outputStream);
} finally {
if (outputStream != null) outputStream.close();
}
} }

导出excel功能使用poi类库实现。至此,页面展示和导出均OK!

DataTables合并单元格(rowspan)的实现思路(多分组分类的情况)的更多相关文章

  1. NPOI操作EXCEL(五)——含合并单元格复杂表头的EXCEL解析

    我们在第三篇文章中谈到了那些非常反人类的excel模板,博主为了养家糊口,也玩命做出了相应的解析方法... 我们先来看看第一类复杂表头: ...... 博主称这类excel模板为略复杂表头模板(蓝色部 ...

  2. 【html】合并单元格,并居中显示文本

     现状: 想要实现的效果: 代码实现: <tr> <td colspan=" align="center">用例失败为0,无测试详情</td ...

  3. Datatables js 复杂表头 合并单元格

    x →Datatables官网← x 项目中用到的Table都是用Datatables插件来搞得: 以前都是生成一般性的table: 近期要生成一些复杂表头,合并单元格之类的: 研究了一下. x 去官 ...

  4. table中tr间距的设定table合并单元格 colspan(跨列)和rowspan(跨行)

    table中的tr的默认display:table-row,虽然可以修改为display:block但是就失去了tr特有的显示效果,如(td自动对齐): 并且在tr中对起设定padding是有用的,可 ...

  5. colspan和rowspan合并单元格

    最近在回顾html的时候,经常碰到一些table标签的问题,其中大多数都是合并单元格,所以在这里记录下自己的探究过程: <table cellpadding="0" cell ...

  6. 一种HTML table合并单元格的思路

    /** * 合并单元格 * @param table1 表格的ID * @param startRow 起始行 * @param col 合并的列号,对第几列进行合并(从0开始).如果传下来为0就是从 ...

  7. JS动态生成表格后 合并单元格

    JS动态生成表格后 合并单元格 最近做项目碰到表格中的单元格合并的问题,需求是这样的,首先发ajax请求 请求回来后的数据 动态生成表格数据,但是生成后如果编号或者(根据其他的内容)有相同时,要合并单 ...

  8. php 数据导出到excel 2种带有合并单元格的导出

    具体业务层面 可能会有所不同.以下两种方式涉及的合并单元格地方有所不同,不过基本思路是一致的. 第一种是非插件版本.可能更容易理解点,基本思路就是 组装table 然后 读取 输出到excel上.缺点 ...

  9. vue中 表头 th 合并单元格,且表格列数不定的动态渲染方法

    吐槽 今天,在vue中遇到 复杂表格的渲染 ,需要合并表头th的单元格,且合并单元格的那列的表头数据是动态数据,也就是不知道会有多少个表头列,而这几个表头列还分了好几个子表头. 这个需求在js里用Ju ...

随机推荐

  1. 20155209 林虹宇Exp2 后门原理与实践

    Exp2 后门原理与实践 实验内容 一.使用netcat获取主机操作Shell,cron启动 使用netcat获取主机操作Shell Win获得Linux Shell 查看win的ip地址 windo ...

  2. 20155226《网络攻防》 Exp5 MSF基础应用

    20155226<网络攻防> Exp5 MSF基础应用 基础问题回答 1.用自己的话解释什么是exploit,payload,encode? exploit : Exploit的英文意思就 ...

  3. 20155234 exp4 恶意代码分析

    实验4 恶意代码分析 系统运行监控 Schtasks 先建立一个netstat20155234.txt文件,在文件中输入 date /t >> c:\netstat20155234.txt ...

  4. mfc 动态为控件添加事件2

    重载窗口过程 为动态控件绑定事件 一.重载窗口过程处理函数 CWnd::WindowProc virtual LRESULT WindowProc( UINT message, WPARAM wPar ...

  5. CF434D Nanami's Power Plant

    就是切糕那道题,首先对每个函数连一串,然后\(x_u\leq x_v+d\)这个条件就是\(u\)函数\(i\)取值连向\(v\)函数\(i-d\)取值边权为inf,然后答案就是最小割了. #incl ...

  6. ASYNC_IO_COMPLETION

    项目组有一个数据库备份的Job运行异常,该Job将备份数据存储到remote server上,平时5个小时就能完成的备份操作,现在运行19个小时还没有完成,backup命令的Wait type是 AS ...

  7. mpvue两小时,产出一个《点钞辅助工具》小程序

    CoffeeScript,Pug,Sass使用 以下内容门槛较高,如看不懂或觉得需要继续了解,结尾处放置了原视频流程与GitHub地址,欢迎琢磨与Star,谢谢. 文章不做技术语法解读,如不清楚,请前 ...

  8. Docker部署Zookeeper容器

    获取zookeeper镜像 docker pull zookeeper 创建zookeeper容器 docker run --name="zookeeper" -p 2181:21 ...

  9. 第二阶段Sprint9

    昨天:重新规划主界面,把视频录制暂放到主页面里 今天:查看有关“共享平台”的资料,看如何实现上传下载功能,并尝试编码, 遇到的问题:看不懂什么意思,照例子做不行,还得需要联网等

  10. 老李的blog使用日记(2)

    寥寥数语结束一个不曾期待的遇见,可还是剧情不会这样结束,他也会在我的时间里注册自己的专属账号,无论什么时候,他会时而需要被注视着,为了达到目的,即使不择手段,只为一次擦肩而过的邂逅,极短的一段时间,相 ...