当前位置: 首页 > news >正文

jxls2.10实现模板导出/单元格合并/自定义标签实现单元格隐藏

目录

    • 前言
    • 需求
    • 项目环境
    • 记住!一定要先把模板里的单元格合并先去掉,防止标签定位有问题!
    • 代码
    • Excel上的标签准备工作
    • jxls标签语句
      • 区域定位
      • 循环渲染
      • 单元格合并(重点)

前言

最近接手一个超紧急项目,需要做Excel导出,这个excel排版非常逆天,以至于我从看到排版的第一眼就认为一定要预先准备一个模板,然后将数据写入进去。另外要说一下,数据也是我通过接口向上游系统请求的。其实啊,本来我也没把这个导出当个事,因为我之前就做过,在固定位置写标签然后写入,工具类都是现成的,不过那个项目用的是jxls1.0.6,那个版本作者已经弃更了,而且会跟poi5.X产生兼容问题,所以我果断选择jxls2.X.

jxls官网地址:https://jxls.sourceforge.net/jxls-2.x/reference/custom_command.html
资料很少,我估计你看了也白看。
请添加图片描述

需求

看到上面那逆天的排版,你就知道,这里面有对象有列表,最需要考虑的是当渲染List数据的时候,左边的单元格合并需要重新合并。第二,我的模板里有些预设的字段可能会是空,客户要求如果是空那么excel上就不展示。这就要求这个模板的有些字段要随着条件的显示。

项目环境

我的项目是基于ruoyi-flex实现的,Spring boot3.2.5,当然这倒是无所谓,poi版本5.2.5(重要,因为3和5会有兼容问题),jxls版本2.10.0

        <dependency>
            <groupId>org.jxls</groupId>
            <artifactId>jxls</artifactId>
            <version>2.10.0</version>
        </dependency>

        <dependency>
            <groupId>org.jxls</groupId>
            <artifactId>jxls-poi</artifactId>
            <version>2.10.0</version>
        </dependency>

上面的excel模板过于逆天,我就不一比一还原了,我就简单做了个模板。而且模板在公司里,也传不出来。
在这里插入图片描述

记住!一定要先把模板里的单元格合并先去掉,防止标签定位有问题!

代码

package com.ruoyi.web.controller;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.StrUtil;
import com.aizuda.easy.retry.common.core.util.JsonUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.util.MapUtils;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.ruoyi.common.core.core.domain.AjaxResult;
import com.ruoyi.common.excel.utils.ExcelUtil;
import com.ruoyi.common.json.utils.JsonUtils;
import com.ruoyi.web.util.HiddenCommand;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.jxls.builder.xls.XlsCommentAreaBuilder;
import org.jxls.common.Context;
import com.ruoyi.common.web.core.BaseController;
import com.ruoyi.web.domain.model.DemoGoods1;
import com.ruoyi.web.service.DemoGoodsService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.jxls.util.JxlsHelper;
import org.springframework.web.bind.annotation.*;

import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/goods")
@Slf4j
@SaIgnore
public class DemoGoodsController extends BaseController {

    public static void main(String[] args) throws IOException {
        Map<String, Object> map = new HashMap<>();
        String userStr = "{\n" +
            "    \"name\": \"李雨桐\",\n" +
            "    \"idcard\": \"1\",\n" +
            "    \"userid\": \"1\",\n" +
            "    \"gw\": \"1\",\n" +
            "    \"sex\": \"1\",\n" +
            "    \"birth\": \"1\",\n" +
            "    \"bm\": \"1\",\n" +
            "    \"bm\": \"1\",\n" +
            "    \"zt\": \"1\"\n" +
            "}";
        JSONObject userObj = JSON.parseObject(userStr);
        map.put("user", userObj);
        String gzzStr = "{\n" +
            "    \"ndkhqk\": \"you\",\n" +
            "    \"lcjlghsl\": \"1\",\n" +
            "    \"hxkhcfzs\": \"2\",\n" +
            "    \"khjlghsl\": \"3\",\n" +
            "    \"yjxhwtsl\": \"4\",\n" +
            "    \"ywccl\": \"5\",\n" +
            "    \"list\": [\n" +
            "        {\n" +
            "            \"khmc\": \"test1\",\n" +
            "            \"sxye\": \"20\",\n" +
            "            \"sxzt\": \"1\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"khmc\": \"test2\",\n" +
            "            \"sxye\": \"20\",\n" +
            "            \"sxzt\": \"1\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"khmc\": \"test3\",\n" +
            "            \"sxye\": \"20\",\n" +
            "            \"sxzt\": \"1\"\n" +
            "        },\n" +
            "        {\n" +
            "            \"khmc\": \"test4\",\n" +
            "            \"sxye\": \"20\",\n" +
            "            \"sxzt\": \"1\"\n" +
            "        }\n" +
            "    ]\n" +
            "}";
        JSONObject gzzObj = JSON.parseObject(gzzStr);
        map.put("gzzl", gzzObj);

        InputStream is = null;
        OutputStream os = null;
        try {
            // 读取模板
            is = DemoGoodsController.class.getClassLoader().getResourceAsStream("测试.xlsx");

            // 新建文件
            File file = new File("E:/export.xlsx");
            // 输出流
            os = new BufferedOutputStream(new FileOutputStream(file));
            //构建数据
            Context context = new Context(map);
            // Context context = buildData();

            XlsCommentAreaBuilder.addCommandMapping(HiddenCommand.COMMAND_NAME, HiddenCommand.class);
            // 数据写入模板
            JxlsHelper.getInstance().processTemplate(is, os, context);
            // }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            is.close();
            os.close();
        }
    }
}

模板就放在springboot静态资源路径里就可以了

自定义标签类实现

package com.ruoyi.web.util;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.jxls.area.Area;
import org.jxls.command.AbstractCommand;
import org.jxls.command.Command;
import org.jxls.common.CellRef;
import org.jxls.common.Context;
import org.jxls.common.Size;
import org.jxls.transform.poi.PoiTransformer;
import org.jxls.util.Util;

public class HiddenCommand extends AbstractCommand {


    public static final String COMMAND_NAME = "hiddenDef";

    public static final String ROW = "row";
    public static final String COL = "col";

    /**隐藏行还是列  行:row, 列:col*/
    private String hidTag;
    /**条件*/
    private String condition;

    private Area area;

    @Override
    public String getName() {
        return COMMAND_NAME;
    }

    @Override
    public Command addArea(Area area) {
        if (!super.getAreaList().isEmpty()) {
            throw new IllegalArgumentException("You can add only a single area to 'merge' command");
        }
        this.area = area;
        return super.addArea(area);
    }

    @Override
    public Size applyAt(CellRef cellRef, Context context) {
        Boolean conditionResult = Util.isConditionTrue(getTransformationConfig().getExpressionEvaluator(), condition, context);
        if (conditionResult) {
            PoiTransformer transformer = (PoiTransformer)this.getTransformer();
            Sheet sheet = transformer.getWorkbook().getSheet(cellRef.getSheetName());

            CellRef srcCell = area.getStartCellRef();
            if(ROW.equals(hidTag)) {
                int rowNub = srcCell.getRow();
                Row row = sheet.getRow(rowNub);
                // short rowHeight = 0;
                row.setZeroHeight(true);
                // row.setHeight(rowHeight);
            }
            if(COL.equals(hidTag)) {
                int col = srcCell.getCol();
                sheet.setColumnHidden(col,true);
            }
        }
        return area.applyAt(cellRef, context);
    }

    public String getHidTag() {
        return hidTag;
    }

    public void setHidTag(String hidTag) {
        this.hidTag = hidTag;
    }

    public String getCondition() {
        return condition;
    }

    public void setCondition(String condition) {
        this.condition = condition;
    }
}

Excel上的标签准备工作

再给你们熟悉一下,我的模拟Excel模板
在这里插入图片描述
我们来看下数据渲染成功后的样子
在这里插入图片描述
我们要考虑:

  1. 单元格的合并
  2. C6那里,基本信息-服务年限如果没有数据要隐藏起来
  3. C10那里,不良授信业务情况是个List数据

jxls标签语句

快捷键shift+F2,如要删除请右击点击删除注释,这个标签语句都是写在注释里的。

区域定位

jx:area(lastCell="F12")

这个注释写在标题头那里即可,我看大家都是这么写的,官网的例子也是的。
在这里插入图片描述
对应的官网文档地址:https://jxls.sourceforge.net/jxls-2.x/reference/xls_area.html
我的理解就是,jxls需要读取表格的作用域范围,你要给他最后的行在哪里,我这里是F12。

请注意:我最后一行有单元格合并,所以我是先把单元格合并给去了,获取到准确的定位,然后再重新定位的,lastCell参数的准确性是一定要保证的!

循环渲染

jx:each(items="gzzl.list" var="ywqk" lastCell="F11")

标签注释写在循环标签的第一个就可以了,关键是lastCell的位置要写对。
在这里插入图片描述

each是循环关键字
items:就是你传入的map的属性
var:变量别名
lastCell作用域的尾部定位(重要)

单元格合并(重点)

jx:mergeCells(rows="gzzl.list.size()+5" lastCell="A12")

标签注释写在对应的title那即可,关键是lastCell的位置要写对。
在这里插入图片描述
mergeCells:合并单元格关键字
rows:合并多少行
lastCell作用域的尾部定位(重要)

我这里合并的逻辑很简单,你对照excel就会明白,因为你的单元格会随着list数据渲染而增多,所以在计算行数的时候是把list的容量和多出来的空行相加(下图选中的部分就是多出来的空行)。
在这里插入图片描述
这里我要强调mergeCell的lastCell,一开始我并不懂lastCell参数在这里怎么写,我就写当前标签的位置,结果就导致渲染数据之后,单元格整体错乱。所以这里lastCell一定要写你要合并到模板的哪里?像我这里就是合并到差错情况那一行,就是A12。同理,管护情况和不良授信业务情况也是同理,管护情况的rows就是gzzl.list.size()+3,不良授信业务情况就是gzzl.list.size()+1
在这里插入图片描述

基本信息那里是合并两列,lastCell就写B6,自动两列合并。

写到这里,如果你的模板是纯静态的,那么你基本上就解决了这个问题,但是我这边改了需求,要求基本信息里的服务年限,如果没有传值那就不展示这个标签。这怎么办?jxls有对应的标签处理吗?很遗憾,没有,jxls里提供的标签太少了,需要自定义标签。我这里的逻辑是把不展示的标签给隐藏起来,我担心删除掉会影响标签读取继而导致表格错乱之类的问题。代码上面已经给了,我们来看excel里面怎么配。
在这里插入图片描述
hidTag是定义的隐藏行还是列,我这里隐藏行。
代码我是参照这篇帖子的:https://blog.csdn.net/fascinatingGirl/article/details/107541969
因为官网自定义标签的文档只有寥寥数字,你就对着官网可劲学吧!
在这里插入图片描述
最后展示的效果:
在这里插入图片描述
能看到,第六行已经被隐藏起来了,这算是一种折中的处理方式,本来嘛如果我没有解决方案的话,我要么就打算展示空标签,反正也不是什么重要的事情跟客户说一下问题也不大。要么就让前端去做,这是若依群给我的方案,说前端可以把渲染的表格导出成excel,我看了几篇帖子好像是可以,但是他们的例子都比较简单,没有我这种左边的列需要合并的。

相关文章:

  • 为什么ASCII的A是65[特殊字符]
  • NET模式下如何配置虚拟机的IP地址为静态的
  • 在Ubuntu 22.04里使用KVM创建虚拟机Ubuntu 22.04
  • 【源码】Mybatis源码
  • 图片文本识别OCR+DeepSeekapi实现提取图片关键信息
  • Linux权限理解
  • [IEEE TIP 2024](cv即插即用模块分享)IdeNet信息增强模块 性能提升必备!
  • 如何用海伦公式快速判断点在直线的哪一侧
  • MCP(模型上下文协议)简单案例
  • XILINX DDR3专题---(1)IP核时钟框架介绍
  • Python及C++中的列表
  • 2025届蓝桥杯JavaB组个人题解(题目全)
  • 【音视频】SDL播放PCM音频
  • 【音视频】SDL渲染YUV格式像素
  • 十六届蓝桥杯嵌入式省赛直播基本外设驱动
  • 设计模式 Day 8:策略模式(Strategy Pattern)完整讲解与实战应用
  • Bartender 5 for Mac 多功能菜单栏管理
  • CSS >子元素选择器和空格
  • 2025年第十六届蓝桥杯省赛C++ A组真题
  • nvm list available 无法查看解决办法
  • 提卡的网站怎么做/长沙弧度seo
  • 石家庄网站建设案例/重庆seo哪个强
  • 百度推广商桥网站上怎么去掉/深圳网络优化公司
  • 重庆建站网站免费/哪里有seo排名优化
  • 学做网站要多久/安卓优化大师手机版
  • 如何进入优容网站/uc搜索引擎入口