【EasyExcel实践】万能导出,一个接口导出多张表以及任意字段(可指定字段顺序)-简化升级版

news2024/10/5 18:54:10

文章目录

  • 前言
  • 正文
    • 一、项目简介
    • 二、核心代码
      • 2.1 pom.xml 依赖配置
      • 2.2 ExcelHeadMapFactory
      • 2.3 ExcelDataLinkedHashMap
      • 2.4 自定义注解 ExcelExportBean
      • 2.5 自定义注解 ExcelColumnTitle
      • 2.6 建造器接口 Builder
      • 2.7 表格工具类 ExcelUtils
      • 2.8 GsonUtil
      • 2.9 模版类 ExportDynamicColumnTemplate
      • 2.10 模版建造器 ExportDynamicColumnTemplateBuilder
    • 三、控制器&调试Bean的定义
      • 3.1 StudentDemo
      • 3.2 NameAndFactoryDemo
      • 3.3 控制器 ExcelDemoController
    • 四、启动类
    • 五、测试

前言

关于万能导出,前一阵子写过一个,功能是实现了的。
就是在使用时,感觉如果需要导出的页面比较多,那就会出现比较多的重复代码。就想着优化+简化一下,能够更方便的使用。

原版代码仓库和这一版的代码仓库相同:
https://gitee.com/fengsoshuai/excel-demo

原版中额外增加了转换器,枚举转换等功能,但是总觉得会让整体功能变复杂,所以在这一版中就去掉了。如果有兴趣看看的话,可以切换到master分支查看。

简化升级的这一版的代码分支是:simple-dynamic-column-export

正文

一、项目简介

本次简化升级,本着对使用者友好的目的去实现的。

另外,本文会粘贴全部代码!!

在这里插入图片描述

在使用上的简化体现在真正导出时,只需要几行代码,就能实现功能。可以让你更加专注于业务参数的组装。而且代码比较简洁。
举个例子:

@GetMapping("/exportDy")
public String exportDy(@RequestParam("table") List<String> table, HttpServletResponse response) throws IOException {

    // 构造导出模版
    ExportDynamicColumnTemplate<StudentDemo> dynamicColumnTemplate = new ExportDynamicColumnTemplateBuilder<>(StudentDemo.class)
            .columnNameList(table)
            .build();
    // 制造假数据
    dynamicColumnTemplate.appendExportDataList(studentDemos());

    // 转换为excel的字节数组
    byte[] bytes = dynamicColumnTemplate.toExcelByteArray();

    // 响应到web
    String fileName = System.currentTimeMillis() + ".xlsx";
    response.setHeader("Content-disposition", "attachment;filename=" + fileName);
    response.setContentType("application/x-msdownload");
    response.setCharacterEncoding("utf-8");
    response.getOutputStream().write(bytes);
    response.getOutputStream().flush();

    return "success";
}

简化后的代码,只需要3步操作:

  1. 根据导出bean,使用建造器生成模版实例;
  2. 给模版实例中填充业务数据;
  3. 填充完数据后,将数据转换为excel格式的字节数组;

当我们抡完这三板斧之后,剩下的就是将字节数组响应到web导出。

二、核心代码

2.1 pom.xml 依赖配置

<dependencies>
  <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.2.0.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.2</version>
    </dependency>


    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>2.2.11</version>

        <exclusions>
            <exclusion>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version>
    </dependency>
</dependencies>

2.2 ExcelHeadMapFactory

表格头映射工厂,提供了注册表格头和获取表格头配置的静态方法。

package org.feng.export.factory;

import org.feng.export.system.ExcelColumnTitle;
import org.feng.export.system.ExcelExportBean;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * 表格头映射工厂
 *
 * @author feng
 */
public class ExcelHeadMapFactory {
    /**
     * 全局表头名映射,key内部变量的变量名和中文名映射为value
     */
    private static final Map<Class<?>, Map<String, String>> HEAD_NAME_MAP = new HashMap<>();


    public static void addHeadClass(Class<?> headClass) {
        if (!headClass.isAnnotationPresent(ExcelExportBean.class)) {
            throw new RuntimeException("HeadClass必须使用注解ExcelExportBean");
        }
        HEAD_NAME_MAP.put(headClass, mapToPrepareHead(headClass));
    }

    public static Map<String, String> getHeadMap(Class<?> headClass) {
        return HEAD_NAME_MAP.get(headClass);
    }

    public static boolean containsHeadClass(Class<?> headClass) {
        return HEAD_NAME_MAP.containsKey(headClass);
    }

    private static Map<String, String> mapToPrepareHead(Class<?> excelHeadClass) {
        Map<String, String> namedMap = new HashMap<>();
        Field[] declaredFields = excelHeadClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            boolean annotationPresent = declaredField.isAnnotationPresent(ExcelColumnTitle.class);
            if (annotationPresent) {
                ExcelColumnTitle excelProperty = declaredField.getAnnotation(ExcelColumnTitle.class);
                String chineseFieldName = excelProperty.value();
                // 保存字段名和中文变量名
                namedMap.put(declaredField.getName(), chineseFieldName);
            }
        }
        return namedMap;
    }
}

2.3 ExcelDataLinkedHashMap

自定义LinkedHashMap,用于实现字段的顺序,以及动态字段展示。
只提供put 和 putAll方法的实现。想要别的put方法的话,读者请自行实现。

package org.feng.export.system;

import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 表格数据专用的map,带顺序,而且初始化的时候,依据指定的表头变量字段名确定导出数据的顺序
 *
 * @author feng
 */
public class ExcelDataLinkedHashMap extends LinkedHashMap<String, Object> {

    private static final long serialVersionUID = -8554095999151235982L;

    /**
     * 头部字段名缓存
     */
    private final Set<String> headColumnNamesCache;

    /**
     * 指定被忽略的列名
     */
    private final Set<String> ignoreColumnSet;

    /**
     * ExcelDataLinkedHashMap构造器
     *
     * @param headColumnNames 表头字段变量名,例如:[name,studentNo,age,className]
     */
    public ExcelDataLinkedHashMap(List<Object> headColumnNames, String... ignoreColumns) {
        if (ignoreColumns != null && ignoreColumns.length > 0) {
            ignoreColumnSet = new HashSet<>(Arrays.asList(ignoreColumns));
        } else {
            ignoreColumnSet = Collections.emptySet();
        }
        // 字段名去重
        List<String> headColumnStringNames = headColumnNames.stream().distinct().map(Object::toString).collect(Collectors.toList());
        // 构建字段名缓存
        this.headColumnNamesCache = new HashSet<>(headColumnStringNames);
        // 指定列数据排列顺序
        for (String headColumnName : headColumnStringNames) {
            this.put(headColumnName, null);
        }
    }

    @Override
    public Object put(String key, Object value) {
        // 只保存字段名缓存中的key以及value
        if (headColumnNamesCache.contains(key)) {
            // 设置了被忽略的列,进行判断处理,匹配到了就不保存
            if (!CollectionUtils.isEmpty(ignoreColumnSet) && ignoreColumnSet.contains(key)) {
                return null;
            }
            return super.put(key, value);
        }
        return null;
    }

    @Override
    public void putAll(Map<? extends String, ?> map) {
        map.forEach(this::put);
    }
}

2.4 自定义注解 ExcelExportBean

package org.feng.export.system;

import java.lang.annotation.*;

/**
 * 指定表格导出的bean
 *
 * @author feng
 */
@Documented
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelExportBean {
}

2.5 自定义注解 ExcelColumnTitle

package org.feng.export.system;

import java.lang.annotation.*;

/**
 * 列名标题注解,标注列的标题
 *
 * @author feng
 */
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelColumnTitle {
    String value();
}

2.6 建造器接口 Builder

package org.feng.export.util;

/**
 * 建造器接口
 *
 * @author feng
 */
public interface Builder<T> {
    T build();
}

2.7 表格工具类 ExcelUtils

提供将数据写入表格的静态方法。

package org.feng.export.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.metadata.WriteSheet;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * excel工具类
 *
 * @author feng
 */
public class ExcelUtils {

    /**
     * 导出数据(单sheet)
     *
     * @param exportData key 是sheet名称,value是每个sheet里面的数据,支持自定义表头
     */
    public static byte[] easyOut(List<Map<String, Object>> exportData) {
        return easyOut("Sheet", exportData);
    }

    /**
     * 导出数据(单sheet)
     *
     * @param exportData key 是sheet名称,value是每个sheet里面的数据,支持自定义表头
     */
    public static byte[] easyOut(String sheetName, List<Map<String, Object>> exportData) {
        return easyOut(Collections.singletonMap(sheetName, exportData));
    }

    /**
     * 导出数据(多sheet)
     *
     * @param exportData key 是sheet名称,value是每个sheet里面的数据,可以自定义
     */
    public static byte[] easyOut(Map<String, List<Map<String, Object>>> exportData) {
        // 导出数据
        ByteArrayOutputStream out = new ByteArrayOutputStream();

        com.alibaba.excel.ExcelWriter excelWriter = EasyExcel.write(out).build();
        int i = 0;
        for (Map.Entry<String, List<Map<String, Object>>> entry : exportData.entrySet()) {
            WriteSheet writeSheet = EasyExcel.writerSheet(i, entry.getKey()).head(head(entry.getValue().get(0))).build();
            i++;
            excelWriter.write(data(entry.getValue(), true), writeSheet);
        }
        excelWriter.finish();

        return out.toByteArray();
    }

    private static List<List<String>> head(Map<String, Object> cellData) {
        List<List<String>> head = new ArrayList<>();
        for (String key : cellData.keySet()) {
            head.add(Collections.singletonList(key));
        }
        return head;
    }

    private static List<List<Object>> data(List<Map<String, Object>> sheetData, boolean skipHead) {
        List<List<Object>> data = new ArrayList<>();
        for (int i = 0; i < sheetData.size(); i++) {
            if (i == 0 && skipHead) {
                continue;
            }
            data.add(new ArrayList<>(sheetData.get(i).values()));
        }
        return data;
    }
}

2.8 GsonUtil

gson工具类,提供json处理、转换的静态方法。

package org.feng.export.util;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;

import java.util.Map;

/**
 * gson工具类
 *
 * @version v1.0
 * @author: fengjinsong
 * @date: 2023年08月26日 15时00分
 */
public class GsonUtil {
    /**
     * 不会序列化空字段的Gson对象
     */
    private static final Gson GSON = new GsonBuilder()
            .create();
    /**
     * 可以序列化空字段的Gson对象
     */
    private static final Gson GSON_WITH_NULL = new GsonBuilder()
            .serializeNulls().create();

    private static final Gson GSON_WITH_DISABLE_HTML_ESCAPING = new GsonBuilder()
            .disableHtmlEscaping().create();

    public static Gson gson() {
        return GSON;
    }

    /**
     * 转换对象为json字符串(不会序列化空字段)
     *
     * @param object 目标对象
     * @return 一个json字符串
     */
    public static String toJson(Object object) {
        return GSON.toJson(object);
    }

    /**
     * 转换对象为json字符串(可以序列化空字段)
     *
     * @param object 目标对象
     * @return 一个json字符串
     */
    public static String toJsonWithNull(Object object) {
        return GSON_WITH_NULL.toJson(object);
    }

    /**
     * 转换对象为json字符串(禁止html转义)
     *
     * @param object 目标对象
     * @return 一个json字符串
     */
    public static String toJsonWithDisableTtmlEscaping(Object object) {
        return GSON_WITH_DISABLE_HTML_ESCAPING.toJson(object);
    }

    public static <T> T fromJson(String jsonStr, Class<T> clazz) {
        return GSON.fromJson(jsonStr, clazz);
    }

    public static Map<String, String> toStringMap(String jsonStr) {
        return GSON.fromJson(jsonStr, new TypeToken<Map<String, String>>() {
        }.getType());
    }

    /**
     * 校验字符串是否是一个json格式
     * <br> 注意:{@code "{}"} 也是符合条件的json
     *
     * @param jsonStr 目标字符串
     * @return true表示目标是一个正确的json格式
     */
    public static boolean validateJson(String jsonStr) {
        JsonElement jsonElement;
        try {
            jsonElement = JsonParser.parseString(jsonStr);
        } catch (Exception e) {
            return false;
        }
        if (jsonElement == null) {
            return false;
        }
        return jsonElement.isJsonObject();
    }
}

2.9 模版类 ExportDynamicColumnTemplate

package org.feng.export;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.feng.export.factory.ExcelHeadMapFactory;
import org.feng.export.system.ExcelDataLinkedHashMap;
import org.feng.export.util.ExcelUtils;
import org.feng.export.util.GsonUtil;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * 导出动态列模版
 *
 * @author feng
 */
@Slf4j
@Getter
public class ExportDynamicColumnTemplate<T> {

    /**
     * 导出的bean类型
     */
    private Class<T> exportBean;

    /**
     * 导出的数据
     */
    private List<Map<String, Object>> exportData;

    /**
     * 当前表头映射
     */
    private Map<String, Object> currentHeadMap;

    /**
     * 真正导出的列名
     */
    private List<Object> realExportColumnNameList;

    /**
     * 是否转换前打印导出数据(含表头)
     */
    @Setter
    private boolean logExportData;

    /**
     * 是否转换前打印实际的表头信息
     */
    @Setter
    private boolean logCurrentHead;

    ExportDynamicColumnTemplate(Class<T> exportBean, List<String> columnNameList) {
        this(exportBean, columnNameList, false, true);
    }

    ExportDynamicColumnTemplate(Class<T> exportBean, List<String> columnNameList, boolean logExportData, boolean logCurrentHead) {
        init(exportBean, columnNameList);
        this.logExportData = logExportData;
        this.logCurrentHead = logCurrentHead;
    }

    /**
     * 追加导出数据
     *
     * @param data          数据
     * @param ignoreColumns 设置忽略的列名
     */
    public void appendExportData(T data, String... ignoreColumns) {
        Objects.requireNonNull(data);
        ExcelDataLinkedHashMap dataMap = new ExcelDataLinkedHashMap(realExportColumnNameList, ignoreColumns);
        exportData.add(dataMap);
        String dataJson = GsonUtil.toJson(data);
        dataMap.putAll(GsonUtil.toStringMap(dataJson));
    }

    /**
     * 追加导出数据列表
     *
     * @param dataList      数据列表
     * @param ignoreColumns 设置忽略的列名
     */
    public void appendExportDataList(List<T> dataList, String... ignoreColumns) {
        if (CollectionUtils.isEmpty(dataList)) {
            return;
        }
        dataList.forEach(data -> appendExportData(data, ignoreColumns));
    }

    /**
     * 将当前需要导出的数据转换为表格的字节数组
     *
     * @return 字节数组
     */
    public byte[] toExcelByteArray() {
        return toExcelByteArray("Sheet");
    }

    /**
     * 将当前需要导出的数据转换为表格的字节数组
     *
     * @param sheetName 指定sheet名
     * @return 字节数组
     */
    public byte[] toExcelByteArray(String sheetName) {
        if (logExportData) {
            logExportData();
        }
        if (logCurrentHead) {
            log.info("导出数据表头信息:{}", currentHeadMap);
        }
        return ExcelUtils.easyOut(sheetName, exportData);
    }


    private void logExportData() {
        for (int i = 0; i < exportData.size(); i++) {
            Map<String, Object> data = exportData.get(i);
            log.info("导出数据[{}]:{}", i, data);
        }
    }

    /**
     * 初始化导出模版信息,主要是表头信息
     *
     * @param exportBean     导出bean的类型
     * @param columnNameList 需要导出的字段名列表
     */
    private void init(Class<T> exportBean, List<String> columnNameList) {
        this.exportBean = exportBean;
        exportData = new ArrayList<>();
        currentHeadMap = new LinkedHashMap<>();

        // 获取代码配置的表头信息
        if (!ExcelHeadMapFactory.containsHeadClass(exportBean)) {
            ExcelHeadMapFactory.addHeadClass(exportBean);
        }
        Map<String, String> headMap = ExcelHeadMapFactory.getHeadMap(exportBean);
        Assert.notEmpty(headMap, "表头不能为空,请检查exportBean的类型");

        // 初始化真正的表头信息,过滤无效配置或找不到的列名
        realExportColumnNameList = new ArrayList<>();
        for (String field : columnNameList) {
            String fieldChineseName = headMap.get(field);
            if (StringUtils.isEmpty(fieldChineseName)) {
                log.info("代码配置的导出表头不完整,不存在字段:{}", field);
            } else {
                currentHeadMap.put(fieldChineseName, field);
                realExportColumnNameList.add(field);
            }
        }

        // 记录当前的表头信息
        exportData.add(currentHeadMap);
    }
}

2.10 模版建造器 ExportDynamicColumnTemplateBuilder

用于生成模版实例。

package org.feng.export;

import lombok.AccessLevel;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.feng.export.system.ExcelExportBean;
import org.feng.export.util.Builder;
import org.springframework.util.Assert;

import java.util.List;

/**
 * 导出动态列模版建造器
 *
 * @author feng
 */
@Accessors(chain = true, fluent = true)
@Setter
public class ExportDynamicColumnTemplateBuilder<T> implements Builder<ExportDynamicColumnTemplate<T>> {

    /**
     * 字段名列表
     */
    private List<String> columnNameList;

    /**
     * 导出的bean类型
     */
    @Setter(AccessLevel.NONE)
    private final Class<T> exportBean;

    /**
     * 是否转换前打印导出数据(含表头)
     */
    private Boolean logExportData;
    /**
     * 是否转换前打印实际的表头信息
     */
    private Boolean logCurrentHead;

    public ExportDynamicColumnTemplateBuilder(Class<T> exportBean) {
        if (!exportBean.isAnnotationPresent(ExcelExportBean.class)) {
            throw new RuntimeException("ExportBean必须使用注解ExcelExportBean");
        }
        this.exportBean = exportBean;
    }

    @Override
    public ExportDynamicColumnTemplate<T> build() {
        check();

        ExportDynamicColumnTemplate<T> template = new ExportDynamicColumnTemplate<>(exportBean, columnNameList);
        if(logCurrentHead != null) {
            template.setLogCurrentHead(logCurrentHead);
        }
        if(logExportData != null) {
            template.setLogExportData(logExportData);
        }
        return template;
    }

    private void check() {
        Assert.notNull(exportBean, "导出的实例类型不能为空");
        Assert.notEmpty(columnNameList, "字段名列表不能为空");
    }
}

三、控制器&调试Bean的定义

这一部分是非核心代码,属于对核心代码使用的一种演示。读者可以按照对应的写法来实现功能。

注意:导出的bean定义,需要使用注解 ExcelExportBean,其中的字段需要使用注解ExcelColumnTitle

3.1 StudentDemo

package org.feng.headbean;

import org.feng.export.system.ExcelColumnTitle;
import lombok.Data;
import org.feng.export.system.ExcelExportBean;

/**
 * 学生demo导出bean
 *
 * @author feng
 */
@Data
@ExcelExportBean
public class StudentDemo {
    @ExcelColumnTitle("姓名")
    private String name;
    @ExcelColumnTitle("年龄")
    private String age;
    @ExcelColumnTitle("性别")
    private String sex;
    @ExcelColumnTitle("学号")
    private String studentNo;
    @ExcelColumnTitle("班级")
    private String className;
}

3.2 NameAndFactoryDemo

该类对本次演示无实际意义,保留着是因为,需要演示加载多个配置表头的实例时的写法。
具体的可以查看启动类ExcelDemoApplication中的内容。

package org.feng.headbean;

import org.feng.export.system.ExcelColumnTitle;
import lombok.Data;
import org.feng.export.system.ExcelExportBean;

/**
 * TODO
 *
 * @author feng
 */
@Data
@ExcelExportBean
public class NameAndFactoryDemo {
    @ExcelColumnTitle("名字")
    private String name;
    @ExcelColumnTitle("工厂")
    private String factory;
}

3.3 控制器 ExcelDemoController

package org.feng.controller;

import org.feng.export.ExportDynamicColumnTemplate;
import org.feng.export.ExportDynamicColumnTemplateBuilder;
import org.feng.headbean.StudentDemo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

@Controller
@RequestMapping("/excel")
public class ExcelDemoController {

    @GetMapping("/exportDy")
    public String exportDy(@RequestParam("table") List<String> table, HttpServletResponse response) throws IOException {

        // 构造导出模版
        ExportDynamicColumnTemplate<StudentDemo> dynamicColumnTemplate = new ExportDynamicColumnTemplateBuilder<>(StudentDemo.class)
                .columnNameList(table)
                .build();
        // 制造假数据
        dynamicColumnTemplate.appendExportDataList(studentDemos());

        // 转换为excel的字节数组
        byte[] bytes = dynamicColumnTemplate.toExcelByteArray();

        // 响应到web
        String fileName = System.currentTimeMillis() + ".xlsx";
        response.setHeader("Content-disposition", "attachment;filename=" + fileName);
        response.setContentType("application/x-msdownload");
        response.setCharacterEncoding("utf-8");
        response.getOutputStream().write(bytes);
        response.getOutputStream().flush();

        return "success";
    }

    private List<StudentDemo> studentDemos() {
        List<StudentDemo> studentDemos = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            StudentDemo studentDemo = new StudentDemo();
            studentDemo.setStudentNo(100 + "-" + i);
            studentDemo.setAge(String.valueOf(20 + i));
            studentDemo.setSex(i > 2 ? "男" : "女");
            studentDemo.setClassName("一班");
            studentDemo.setName("小米" +(i+1));
            studentDemos.add(studentDemo);
        }

        return studentDemos;
    }
}

四、启动类

启动项目时,加载代码配置信息。

package org.feng;

import org.feng.export.factory.ExcelHeadMapFactory;
import org.feng.headbean.NameAndFactoryDemo;
import org.feng.headbean.StudentDemo;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.ArrayList;
import java.util.List;

@SpringBootApplication
public class ExcelDemoApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(ExcelDemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        List<Class<?>> needRegisterExcelHeadClassList = new ArrayList<>();
        needRegisterExcelHeadClassList.add(NameAndFactoryDemo.class);
        needRegisterExcelHeadClassList.add(StudentDemo.class);
        needRegisterExcelHeadClassList.forEach(ExcelHeadMapFactory::addHeadClass);
    }
}

五、测试

在谷歌浏览器访问:
http://localhost:8080/excel/exportDy?table=name,className,studentNo

会下载得到这样的文件:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1326456.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

金蝶云星空业务对象标识是否可以修改

文章目录 金蝶云星空业务对象标识是否可以修改业务背景说明根本原因开发规范终极临时解决方案 金蝶云星空业务对象标识是否可以修改 业务背景 开发人员不注意&#xff0c;新建业务对象或者直接扩展标准产品的业务对象就直接操作保存&#xff0c;然后再次打开界面发现标识已经…

数字生态文明:构建可持续发展的未来

数字技术的快速发展给人类社会带来了巨大的变革,同时也对生态环境产生了深远的影响。在这个背景下,数字生态文明的概念应运而生,它强调在数字时代实现经济、社会和环境的协调发展,构建可持续的未来。 一、数字生态文明的内涵 数字生态文明是指在数字经济发展过程中,遵循…

【数据库】函数依赖

什么是函数依赖 就是在具体的表中/问题中&#xff0c;哪个属性决定另外几个属性。 A属性值相同的时候&#xff0c;能否决定唯一的B U {学号&#xff0c;姓名&#xff0c;年龄&#xff0c;班号&#xff0c;班长&#xff0c;课号&#xff0c;成绩} 就有&#xff1a; ‘学号’ 决…

vxe-table 修改[表尾数据]footer的高度

下面展示一些 内联代码片。 <style> .vxe-table--render-default.size--small .vxe-footer--column.col--ellipsis {height: 20px; } </style>

ElementUI中修改el-table的滚动条样式

注意&#xff1a;本文仅基于webkit引擎浏览器&#xff1b; 如果是火狐浏览器&#xff0c;则是-moz-&#xff1b; 部分webkit引擎浏览器&#xff1a;Google Chrome谷歌浏览器、Safari浏览器、搜狗高速浏览器、QQ浏览器、360极速浏览器等… 当内容超出容器时会出现滚动条&#…

Modbus转Profinet网关的解决方案推荐

现场问题&#xff1a;现场PLC的上端接的是显示器&#xff0c;下端接多台温湿度仪器&#xff0c;但是温湿度仪器的数量超过PLC的插槽限制了&#xff0c;导致项目无法正常完工。 解决方案&#xff1a;在PLC的下端加入Modbus转Profinet网关&#xff08;XD-MDPN100/2000&#xff09…

为外来邮件设置警示消息

大家好&#xff0c;才是真的好。 新版本发布&#xff0c;我们总有很多新内容要讲。其中最重要的就是新功能的测试和介绍。今天我们就来介绍Domino 14中设置外来邮件的提示文本信息。 如果你的Domino服务器环境已经升级到14.0&#xff0c;就可以在服务器的配置文档当中&#x…

怎么放大图片保持清晰度?

怎么放大图片保持清晰度&#xff1f;在生活中我们可能会保存各种各样的图片&#xff0c;但有时保存下来的图片可能太小了&#xff0c;尺寸和像素都不符合自己的要求&#xff0c;当图片像素和尺寸都过小会带来各种缺点&#xff0c;首先就是当我们看图片的时候会感觉它很模糊&…

Hal深入实战/perfetto-systrace实战/SurfaceFlinger合集-安卓framework开发实战开发

背景 hi&#xff0c;粉丝朋友们&#xff1a; 大家好&#xff01; 下面来介绍一下新的framework专题halperfettosurafceflinger&#xff0c;这个专题主要就是分为3大块&#xff0c;但是彼此直接又是相互关联的。 比如surfaceflingre模块深入分析需要用到hal相关的模块&#xff…

3分钟搞懂北交所交易规则和手续费

北交所是2021年11月15日正式开市的新设证券交易所&#xff0c;主要承接全国股转系统精选层挂牌公司的平移上市&#xff0c;以及符合条件的新股上市。 1、北交所的交易规则&#xff1a; &#xff08;1&#xff09;北交所实行30%的涨跌幅限制&#xff1b; &#xff08;2&#…

虚拟机无法进入系统问题

概述 客户在华为云平台上创建了两台虚拟机并部署aarch64 V10 OS&#xff0c;2021-10-28其中一台虚拟机业务出现异常&#xff0c;运维重启虚拟机后系统进不去&#xff0c;左上角光标闪烁&#xff0c;接着重启另一台虚拟机同样起不来&#xff0c;现象一致。 分析 通过分析现场…

进阶之路:高级Spring整合技术解析

Spring整合 1.1 Spring整合Mybatis思路分析1.1.1 环境准备步骤1:准备数据库表步骤2:创建项目导入jar包步骤3:根据表创建模型类步骤4:创建Dao接口步骤5:创建Service接口和实现类步骤6:添加jdbc.properties文件步骤7:添加Mybatis核心配置文件步骤8:编写应用程序步骤9:运行程序 1.…

【案例】图片预览

效果图 如何让图片放大&#xff0c;大多数的UI组件都带有这种功能&#xff0c;今天给大家介绍的这个插件除了放大之外&#xff0c;还可以旋转、移动、翻转、旋转、二次放大&#xff08;全屏&#xff09; 实现 npm i v-viewer -Smain.js 中引入 import viewerjs/dist/viewer.c…

Go后端开发 -- 环境搭建

Go后端开发 – 环境搭建 文章目录 Go后端开发 -- 环境搭建一、环境配置二、IDE的选择三、使用go mod构建项目1.初始化项目2.添加依赖项3.运行项目 四、环境报错1.VS Code中gopls报错 一、环境配置 Go官网下载地址&#xff1a;https://golang.org/dl/ https://go.dev/dl/ Go官方…

javascript读取RFID卡号源码

本示例使用的读卡器&#xff1a;https://item.taobao.com/item.htm?spma1z10.5-c-s.w4002-21818769070.35.74185b43tGWQH5&id562957272162 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-…

【pentaho】kettle读取Hive表不支持bigint和timstamp类型解决。

一、bigint类型 报错: Unable to get value BigNumber(16) from database resultset显示kettle认为此应该是decimal类型(kettle中是TYPE_BIGNUMBER或称BigNumber)&#xff0c;但实际hive数据库中是big类型。 修改kettle源码解决&#xff1a; kettle中java.sql.Types到kettle…

记录SpringBoot包找不到主清单属性问题

之前从来没在意过这个问题&#xff0c;无数次项目打包都没有问题&#xff0c;突然有一天新建了个springboot项目打包部署的时候报错&#xff1a;no main manifest attribute, in xxxx-0.0.1-SNAPSHOT.jar 本明白什么原因&#xff0c;貌似也知道怎么去解决&#xff0c;以为是小…

vue3 在vite.config中无法使用import.meta.env.*的解决办法

第一种,优先使用第一种方法,其中参数mode就是自定义--mode的值,如果没写,就是production或development import { loadEnv } from vite export default ({ mode }) > {return defineConfig({plugins: [vue()],base:loadEnv(mode, process.cwd()).VITE_APP_NAME}) } 第二种 …

【Eachrts】水滴图

引入依赖 npm安装echarts、echarts-liquidfill插件 "echarts": "^5.4.2", "echarts-liquidfill": "^3.1.0",引入插件 import * as echarts from echarts; import echarts-liquidfill;示例 <template><div class"Liqu…

JSON Web Token JWT几种简单的绕过方法

JWT结构 JSON Web Token&#xff08;JWT&#xff09;是一个非常轻巧的规范。 这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。 JWT常被用于前后端分离&#xff0c;可以和Restful API配合使用&#xff0c;常用于构建身份认证机制 如图为JWT加密后的示例&…