直接上代码,原理之前的随笔已经讲过了。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. Scala学习(二)--- 控制结构和函数

    控制结构和函数 摘要: 本篇主要学习在Scala中使用条件表达式.循环和函数,你会看到Scala和其他编程语言之间一个根本性的差异.在Java或C++中,我们把表达式(比如3+4)和语句(比如if语句 ...

  2. 洛咕 P3704 [SDOI2017]数字表格

    大力推式子 现根据套路枚举\(\gcd(i,j)\) \(ans=\Pi_{x=1}^nfib[x]^{\sum_{i=1}^{n/x}\sum_{j=1}^{n/x}[\gcd(i,j)=1]}\) ...

  3. Centos7下python3安装pip-9.0.1

    pip类似RedHat里面的yum,安装Python包非常方便.本节详细介绍pip的安装.以及使用方法 1.下载pip安装包 [root@localhost ~]# wget https://pypi ...

  4. Js_数组操作

    用 js有很久了,但都没有深究过js的数组形式.偶尔用用也就是简单的string.split(char).这段时间做的一个项目,用到数组的地方很多,自以为js高手的自己居然无从下手,一下狠心,我学!呵 ...

  5. Mysql_游标

    MySQL中的游标是一个十分重要的概念.游标提供了一种对从表中检索出的数据进行操作的灵活手段,就本质而言,游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制.MySQL中的游标的语法 ...

  6. POJ 2431 (优先队列)

    题目链接:https://vjudge.net/problem/POJ-2431 思路: “ 在卡车行驶途中, 只有经过加油站才能加油.” 我们不妨转变思路, 理解成“当卡车驶过加油站时就获得了加油的 ...

  7. 统计学习方法c++实现之六 支持向量机(SVM)及SMO算法

    前言 支持向量机(SVM)是一种很重要的机器学习分类算法,本身是一种线性分类算法,但是由于加入了核技巧,使得SVM也可以进行非线性数据的分类:SVM本来是一种二分类分类器,但是可以扩展到多分类,本篇不 ...

  8. Symfony中Doctrine对应的Mongodb数据类型 data type

    1. hash 就是 json对象 2. collection 就是 数组 3. 若要知道如何使用referenceOne, referenceMany, embbedDocument等 主要查看: ...

  9. python中eval方法的使用

    eval函数就是实现list.dict.tuple与str之间的转化str函数把list,dict,tuple转为为字符串# 字符串转换成列表a = "[[1,2], [3,4], [5,6 ...

  10. Day Seven

    站立式会议 站立式会议内容总结 331 今天完成: 1.主页面 toolbar 菜单修改为点击弹出两个选项:新增计划和书籍 2.点击新增书籍跳转到文件管理器 home按钮为回退至上级目录,后退按钮为c ...