直接上代码,原理之前的随笔已经讲过了。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. .net 设置Webbowser 版本

    .net 里的Webbowser控件默认情况是用IE7来渲染 可修改注册表试用是最新的版本来渲染: using System; using System.Collections.Generic; us ...

  2. 20155320 Exp3 免杀原理与实践

    20155320 Exp3 免杀原理与实践 免杀 一般是对恶意软件做处理,让它不被杀毒软件所检测.也是渗透测试中需要使用到的技术. [基础问题回答] (1)杀软是如何检测出恶意代码的? 1.通过行为检 ...

  3. # RocEDU.课程设计2018 第三周进展 博客补交

    RocEDU.课程设计2018 第三周进展 博客补交 本周计划完成的任务 (1).本周计划完成在平板电脑上实现程序的功能,跟第二周计划完成任务基本相似. 本周实际完成情况 (1).实际完成情况还差最后 ...

  4. Luogu P2577 [ZJOI2005]午餐

    一道贪心+类背包DP的好题 首先发现一个十分显然的性质,没有这个性质整道题目都难以下手: 无论两队的顺序如何,总是让吃饭慢的人先排队 这是一个很显然的贪心,因为如果让吃饭慢的排在后面要更多的时间至少没 ...

  5. 对JSON传递图片Base64编码的一点总结

    项目中跟Java对接的时候需要传输图片,经过Base64编码后传输的. 但是实际调试的时候发现Java那边始终无法正常解析出图片. 冷静想想之后,发现问题在于使用OpenCV读取图片,编码的是Mat: ...

  6. StringUtils类方法归纳

    StringUtils方法概览 IsEmpty/IsBlank - checks if a String contains text IsEmpty/IsBlank – 检查字符串是否有内容. Tri ...

  7. QT要点

    1. QT设计器最终会被解释为ui_**.h. 2. QString与init之间的转换: QString转int: bool bIsOk; int a = str.toInt( &bIsOk ...

  8. Docker_容器化gitlab

    Docker部署接口自动化持续集成环境第一步,容器化一个Gitlab! 1:开放防火墙端口 sudo yum install curl openssh-server openssh-clients p ...

  9. 前端项目模块化的实践3:使用 TypeScript 的收益

    以下是关于前端项目模块化的实践,包含以下内容: 搭建 NPM 私有仓库管理源码及依赖: 使用 Webpack 打包基础设施代码: 使用 TypeScript 编写可靠类库 使用 TypeScript ...

  10. 萌新程序媛的首个作品,基于NoSQL的内容管理及低码开发平台

    尽管入行有一段时间了,但之前还从来没想过要开发一款完整的软件产品.这个我跟朋友开发的第一款软件,希望大家帮我们多宣传推广.首个版本肯定有很多的不足,大家也给我们多提意见,还有很多规划中的功能要在之后的 ...