IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 记一次在线展示excel时出现合并单元格的问题 -> 正文阅读

[JavaScript知识库]记一次在线展示excel时出现合并单元格的问题

??最近项目中遇到一个需要解析Excel并在线展示的功能,excel的内容不是固定的,是需要事先通过专用的工具生成xml模板导入到系统中,然后系统解析上报的excel文件时就可以根据模板来解析内容。

??其中生成的xml模板保存了针对每一个单元格的一些属性,所以excel解析不是难事,关键是页面展示excel内容的时候有个合并单元格的需求,这个着实让我苦恼了几天,上下班的路上都在思考这个问题,后来终于想到一种方案,这里记录一下。

??因为excel内容不是固定的,所以解析与展示数据时,我都是以单元格为单位的,所以这里我的实体类对象是:

public class TemplateItem {

    private Long id;

    /** 列位置 **/
    private String col;

    /** 列合并数 **/
    private String colSpan;

    /** 行位置 **/
    private String row;

    /** 行合并数 **/
    private String rowSpan;

    /** 数据类型(0标签 1指标值 2计算项) **/
    private String contentEnum;

    /** 指标信息 **/
    private String content;
}

??返回给前端展示的时候,要么返回每一个单元格,这样直接前端通过<table></table>标签或者Bootstrap Table插件画出即可;要么只返回有数据的单元格,需要前端计算出单元格具体的展示位置。

??既然是后端开发(虽然前端也是自己写),那我就把计算的逻辑放后端了,而且对比了一下,感觉前端计算好像也不是太方便。然后问题就来了,存在合并单元格的情况时我怎么计算每个单元格的具体位置。首先明确的是,前端直接采用<table></table>标签来实现,虽然两种都实现了,但是Bootstrap Table在合并单元格的时候似乎不是很好用,当初采用只是为了样式美观😓,<table></table>标签则可直接通过<td>rowspan、colspan属性来控制合并的行数和列数。

------------------------------------------------ 重点来了 ------------------------------------------------
??使用<table></table>来画excel的内容,也就是我返回是数据格式得是一个二维数组,因为<table></table>标签是使用<tr>、<td>来画表格的,这样我数组里每一项放那个单元格对象即可。

??在组装二维数组的时候,一开始我总是想着在没有数据的位置拼上空的单元格对象,但是感觉计算好复杂,因为合并多行多列的情况都有,这样计算的时候要考虑很多。后来一想,干脆我反过来做,因为已经知道一共有多少行多少列了,我先按照一张空白excel的样子,先画出每一个单元格(先组装一个空的二维数组),然后根据已有的数据项信息,删除掉被合并的单元格(删除被合并的数组项),这样不就能很容易得出来想要的二维数组了吗。

上代码:

两个实体类TemplateTemplateItem,其中Template存储了这一套模板的属性信息,TemplateItem存储了模板里每一个单元格的属性信息。
Template对象:

package com.example.demo.excel;

import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

/**
 * 模板
 *
 * @author zzs
 * @date 2021/12/7 17:16
 */
@Getter
@Setter
public class Template implements Serializable {

    private Long id;

    /** 模板名称 **/
    private String templateName;

    /** 行数 **/
    private String rowCount;

    /** 列数 **/
    private String colCount;

    /** 表头行数 **/
    private Integer headCount;

    /** 备注 **/
    private String remark;

    private Boolean isDel;

    private Long createBy;

    private Date createTime;

    private Long updateBy;

    private Date updateTime;

    /** 模板项列表 **/
    private List<TemplateItem> items;

    private static final long serialVersionUID = 1L;
}

TemplateItem对象:

package com.example.demo.excel;

import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;

/**
 * 模板项
 *
 * @author zzs
 * @date 2021/12/7 17:11
 */
@Getter
@Setter
public class TemplateItem implements Serializable {

    private Long id;

    /** 模板id **/
    private Long templateId;

    /** 列位置 **/
    private String col;

    /** 列合并数 **/
    private String colSpan;

    /** 行位置 **/
    private String row;

    /** 行合并数 **/
    private String rowSpan;

    /** 数据类型(0标签 1指标值 2计算项) **/
    private String contentEnum;

    /** 指标信息 **/
    private String content;

    /** 公式 **/
    private String expstr;

    /** 是否特指单位 **/
    private String isUnitConver;

    /** 特指单位 **/
    private String eleUnit;

    /** 自定义详细样式 **/
    private String cssClass;

    /** 自定义格式化 **/
    private String formatString;

    /** 是否只读 **/
    private String isReadonly;

    private static final long serialVersionUID = 1L;
}

业务方法:
其中二维数组我是用guavaTable来代替,这个使用起来比较方便。

package com.example.demo.excel;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import org.springframework.stereotype.Service;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author zzs
 * @date 2021/12/7 17:12
 */
@Service
public class TemplateService {

    /**
     * 入口
     */
    public List<List<TemplateItem>> getFormData() {
        // 这里就不展示数据的获取了
        Template template = new Template();
        List<TemplateItem> items = template.getItems();

        HashBasedTable<Integer, Integer, TemplateItem> dataTable = HashBasedTable.create();
        items.stream().forEach(item -> {
            Integer row = Integer.valueOf(item.getRow());
            Integer col = Integer.valueOf(item.getCol());
            dataTable.put(row, col, item);
        });
        Integer rowCount = Integer.valueOf(template.getRowCount());
        Integer colCount = Integer.valueOf(template.getColCount());
        List<List<TemplateItem>> dataList = fillTableCell(rowCount, colCount, dataTable);

        return dataList;
    }

    /** 填充表格 **/
    private List<List<TemplateItem>> fillTableCell(int rowCount, int colCount,
                                                   Table<Integer, Integer, ? extends TemplateItem> dataTable) {
        // 初始化一个空的table
        HashBasedTable<Integer, Integer, TemplateItem> fullTable = HashBasedTable.create();
        for (int i = 1; i <= rowCount; i++) {
            for (int j = 1; j <= colCount; j++) {
                fullTable.put(i, j, new TemplateItem());
            }
        }

        // 排序
        List<List<? extends TemplateItem>> dataList = dataTable.rowMap().entrySet().stream()
                .sorted(Comparator.comparing(Map.Entry::getKey))
                .map(entry -> entry.getValue().entrySet().stream()
                        .sorted(Comparator.comparing(Map.Entry::getKey))
                        .map(Map.Entry::getValue)
                        .collect(Collectors.toList()))
                .collect(Collectors.toList());
        // 将实际的值存储到table中的cell
        for (List<? extends TemplateItem> rowData : dataList) {
            for (TemplateItem templateItem : rowData) {
                Integer row = Integer.valueOf(templateItem.getRow());
                Integer col = Integer.valueOf(templateItem.getCol());
                Integer rowSpan = Integer.valueOf(templateItem.getRowSpan());
                Integer colSpan = Integer.valueOf(templateItem.getColSpan());
                fullTable.put(row, col, templateItem);

                // 判断要合并的cell
                if (rowSpan == 1 && colSpan == 1) {
                    continue;
                }
                if (rowSpan > 1 && colSpan == 1) {
                    for (int i = 1; i < rowSpan; i++) {
                        fullTable.remove(row + i, col);
                    }
                    continue;
                }
                if (rowSpan == 1 && colSpan > 1) {
                    for (int i = 1; i < colSpan; i++) {
                        fullTable.remove(row, col + i);
                    }
                    continue;
                }
                if (rowSpan > 1 && colSpan > 1) {
                    for (int i = 0; i < rowSpan; i++) {
                        for (int j = 0; j < colSpan; j++) {
                            if (i == 0 && j == 0) {
                                continue;
                            }
                            fullTable.remove(row + i, col + j);
                        }
                    }
                    continue;
                }
            }
        }

        // 转换为输出的格式
        return fullTable.rowMap().entrySet().stream()
                .map(rowEntry -> rowEntry.getValue().entrySet().stream()
                        .map(colEntry -> colEntry.getValue())
                        .collect(Collectors.toList()))
                .collect(Collectors.toList());
    }
}

这样返回的集合在前端直接遍历画出每一行<tr>每一个<td>即可。

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-12-08 13:43:31  更:2021-12-08 13:43:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:36:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码