直接上代码,原理之前的随笔已经讲过了。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. c# 多线程和异步

    有时候需要多线程来运行处理后端和UI的更新. 通常用 ((Action)delegate() { if (isBussy) { return; } isBussy = true; while (tru ...

  2. 20155209林虹宇 Exp7 网络欺诈防范

    Exp7 网络欺诈防范 简单应用SET工具建立冒名网站 kali要作为web服务器让靶机访问冒名网站,所以要使用阿帕奇web服务器软件. 要阿帕奇使用80端口.进入配置文件/etc/apache2/p ...

  3. Exp4 恶意代码分析 20155223

    Exp4 恶意代码分析 20155223 使用原生命令schtasks监视系统运行 在系统盘目录下建立脚本文件netstatlog.bat,包含以下命令: date /t >> c:\ne ...

  4. 20155237 《JAVA程序设计》实验三(敏捷开发与XP实践)实验报告

    20155237 <JAVA程序设计>实验三(敏捷开发与XP实践)实验报告 实验内容 敏捷开发与XP实践 XP基础 XP核心实践 相关工具 实验要求 1.没有Linux基础的同学建议先学习 ...

  5. 2017-2018-1 20155331 嵌入式C语言

    2017-2018-1 20155331 嵌入式C语言 作业要求: 在作业本上完成附图作业,要认真看题目要求. 提交作业截图 作弊本学期成绩清零(有雷同的,不管是给别人传答案,还是找别人要答案都清零) ...

  6. LOJ#6354. 「CodePlus 2018 4 月赛」最短路[最短路优化建图]

    题意 一个 \(n\) 个点的完全图,两点之间的边权为 \((i\ xor\ j)*C\) ,同时有 \(m\) 条额外单向路径,问从 \(S\) 到 \(T\) 的最短路. \(n\leq 10^5 ...

  7. .Net单元测试业务实践

    使用次数和允许取消次数单元测试实践 /** * prism.js Github theme based on GitHub's theme. * @author Sam Clarke */ code[ ...

  8. stl源码剖析 详细学习笔记 hashtable

    //---------------------------15/03/24---------------------------- //hashtable { /* 概述: sgi采用的是开链法完成h ...

  9. http-cache浏览器缓存

    摘至知乎 首先得明确 http 缓存的好处 减少了冗余的数据传输,减少网费 减少服务器端的压力 Web 缓存能够减少延迟与网络阻塞,进而减少显示某个资源所用的时间 加快客户端加载网页的速度 常见 ht ...

  10. keycode值对照表

    转载自:https://segmentfault.com/a/1190000005828048 字母和数字键的键码值(keyCode) 按键 键码 按键 键码 按键 键码 按键 键码 A 65 J 7 ...