工具类实现导出复杂excel、word

news2025/1/11 16:51:29

1、加入准备的工具类

package com.ly.cloud.utils.exportUtil;



import java.util.Map;

public interface TemplateRenderer {

    Writable render(Map<String, Object> dataSource) throws Throwable;

}
package com.ly.cloud.utils.exportUtil;

import java.util.Map;

public interface ExportedFileNameFactory {

    String getName(Map<String, Object> dataSource);

}
package com.ly.cloud.utils.exportUtil;

import java.io.IOException;
import java.io.OutputStream;

public interface Writable {

    void write(OutputStream outputStream) throws IOException;

}
package com.ly.cloud.utils.exportUtil;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.util.Map;

public abstract class AbsExporter implements TemplateRenderer {

    private static final Logger LOGGER = LoggerFactory.getLogger(AbsExporter.class);

    public void doExport(Map<String, Object> dataSource, File exportedFile) throws Throwable {
        try(FileOutputStream fos = new FileOutputStream(exportedFile)) {
            Writable writable = this.render(dataSource);
            writable.write(fos);
        }
    }

    public abstract String getTargetFileSuffix();

    public void afterExport() {}
}
package com.ly.cloud.utils.exportUtil;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.zip.ZipOutputStream;

public class ExportProcess {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExportProcess.class);

    /**
     * 要导出的数据源
     */
    private List<Map<String, Object>> dataSourceList = new ArrayList<>();

    /**
     * 是否为多文件导出
     */
    private boolean multiFile;

    /**
     * 导出器
     */
    private AbsExporter exporter;

    /**
     * 导出文件名
     */
    private String exportedFilename;

    /**
     * 导出为多文件时的文件名命名工厂
     */
    private ExportedFileNameFactory nameFactory;

    private ExportProcess(Map<String, Object> dataSource, AbsExporter exporter, String exportedFilename) {
        this.dataSourceList.add(dataSource);
        this.multiFile = false;
        this.exporter = exporter;
        this.exportedFilename = exportedFilename;
    }

    private ExportProcess(List<Map<String, Object>> dataSourceList, AbsExporter exporter, String exportedFilename, ExportedFileNameFactory nameFactory) {
        this.dataSourceList.addAll(dataSourceList);
        this.multiFile = true;
        this.exporter = exporter;
        this.exportedFilename = exportedFilename;
        this.nameFactory = nameFactory;
    }

    public static ExportProcess newProcess(Map<String, Object> dataSource, AbsExporter exporter, String exportedFilename) {
        return new ExportProcess(dataSource, exporter, exportedFilename);
    }

    public static ExportProcess newProcess(List<Map<String, Object>> dataSourceList, AbsExporter exporter, String exportedFilename, ExportedFileNameFactory nameFactory) {
        return new ExportProcess(dataSourceList, exporter, exportedFilename, nameFactory);
    }

    public ExportResult export() {
        ExportResult exportResult = new ExportResult(this.multiFile ? exportAsZipFile() : exportAsSingleFile());
        this.exporter.afterExport();
        return exportResult;
    }

    /**
     * 导出为单文件
     * @return 导出结果
     */
    private File exportAsSingleFile() {
        Map<String, Object> dataSource = this.dataSourceList.get(0);
        // 导出文件所在目录路径
        String exportedFileDirPath = FileUtils.filePathJoin(FileUtils.TEMP_FILE_PATH, "exportedFileDir" + UUID.randomUUID().toString());
        // 创建导出文件所在目录
        File exportedFileDir = FileUtils.createDir(exportedFileDirPath);
        String exportedFilePath = FileUtils.filePathJoin(exportedFileDirPath, this.exportedFilename + this.exporter.getTargetFileSuffix());
        File exportedFile = new File(exportedFilePath);
        try {
            this.exporter.doExport(dataSource, exportedFile);
            return exportedFile;
        } catch (Throwable t) {
            LOGGER.error(t.getMessage(), t);
            FileUtils.deleteDir(exportedFileDir);
        }
        return null;
    }

    /**
     * 导出为压缩文件
     * @return 导出结果
     */
    private File exportAsZipFile() {
        String tempFileDirPath = FileUtils.filePathJoin(FileUtils.TEMP_FILE_PATH, "tempFile" + UUID.randomUUID().toString());
        File tempFileDir = FileUtils.createDir(tempFileDirPath);
        // 导出文件所在目录路径
        String exportedFileDirPath = FileUtils.filePathJoin(FileUtils.TEMP_FILE_PATH, "exportedFileDir" + UUID.randomUUID().toString());
        // 创建导出文件所在目录
        File exportedFileDir = FileUtils.createDir(exportedFileDirPath);
        File exportedFile = new File(FileUtils.filePathJoin(exportedFileDirPath, this.exportedFilename + ".zip"));
        try {
            for (Map<String, Object> dataSource : this.dataSourceList) {
                this.exporter.doExport(dataSource, new File(FileUtils.filePathJoin(tempFileDirPath, this.nameFactory.getName(dataSource) + this.exporter.getTargetFileSuffix())));
            }
            try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(exportedFile));
                 BufferedOutputStream bos = new BufferedOutputStream(out)) {
                FileUtils.zipDir(tempFileDirPath, out, bos);
            }
            return exportedFile;
        } catch (Throwable t) {
            LOGGER.error(t.getMessage(), t);
            FileUtils.deleteDir(exportedFileDir);
        } finally {
            FileUtils.deleteDir(tempFileDir);
        }
        return null;
    }
}
package com.ly.cloud.utils.exportUtil;


import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;

public class ExportResult {

    private static final Logger LOGGER = LoggerFactory.getLogger(ExportResult.class);

    private File exportedFile;

    ExportResult(File exportedFile) {
        this.exportedFile = exportedFile;
    }

    public File getExportedFile() {
        if (null == this.exportedFile) {
            throw new NullPointerException("exportedFile 为 null");
        }
        return exportedFile;
    }

    public void download(HttpServletRequest request, HttpServletResponse response) {
        File exportedFile = getExportedFile();
        // 用于清除首部的空白行
        response.reset();
        response.setContentType("application/x-download; charset=utf-8");
        setFileDownloadHeader(request, response, this.exportedFile.getName());
        doDownload(response, exportedFile);
    }

    private void setFileDownloadHeader(HttpServletRequest request, HttpServletResponse response, String filename) {
        //获取浏览器信息
        String ua = request.getHeader("USER-AGENT");
        //转成UserAgent对象
        UserAgent userAgent = UserAgent.parseUserAgentString(ua);
        //获取浏览器信息
        Browser browser = userAgent.getBrowser();
        //浏览器名称
        String browserName = browser.getName();
        String encodedFilename;
        try {
            encodedFilename = URLEncoder.encode(filename, "UTF8");
            if (StringUtils.contains(browserName, "Internet Explorer")) {
                response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFilename + "\"");
            } else if (StringUtils.contains(browserName, "Chrome") || StringUtils.contains(browserName, "Firefox")) {
                response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFilename);
            } else {// 其他浏览器
                response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFilename + "\"");
            }
        } catch (UnsupportedEncodingException e) {
            LOGGER.error(e.getMessage(), e);
        }
    }

    private void doDownload(HttpServletResponse response, File exportedFile) {
        OutputStream os = null;
        byte[] buffer = new byte[1024];
        BufferedInputStream bis = null;
        FileInputStream exportedFileInputStream = null;
        try {
            exportedFileInputStream = new FileInputStream(exportedFile);
            response.addHeader("content-length", exportedFileInputStream.available() + "");
            os = response.getOutputStream();
            bis = new BufferedInputStream(exportedFileInputStream);
            int i = bis.read(buffer);
            while (i != -1) {
                os.write(buffer, 0, i);
                i = bis.read(buffer);
            }
            os.flush();
        } catch (IOException e) {
            LOGGER.error(e.getMessage(), e);
        } finally {
            if (exportedFileInputStream != null) {
                try {
                    exportedFileInputStream.close();
                } catch (IOException e) {
                    LOGGER.error(e.getMessage(), e);
                }
            }
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    LOGGER.error(e.getMessage(), e);
                }
            }
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    LOGGER.error(e.getMessage(), e);
                }
            }
            // 下载完成后删除临时文件
            if (exportedFile.exists()) {
                File exportedFileDir = exportedFile.getParentFile();
                FileUtils.deleteDir(exportedFileDir);
            }
        }
    }
}
package com.ly.cloud.utils.exportUtil;

import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;


public class FileUtils {

    static {
        // 当文件系统中没有nhtemp文件夹的时候,创建
        File sf = new File(FileUtils.filePathJoin(System.getProperty("java.io.tmpdir"), "nhtemp"));
        if (!sf.exists()) {
            sf.mkdirs();
        }
    }

    /**
     * 临时文件夹,在临时文件夹中创建nhtemp,用来保存所有使用本工具类创建的文件,以便于统一清空临时文件夹,并且已经包含了文件分割符号,请注意
     */
    public static final String TEMP_FILE_PATH = FileUtils.filePathJoin(System.getProperty("java.io.tmpdir"), "nhtemp");

    /**
     * 向文件写入数据
     *
     * @param is
     * @param file
     * @throws IOException
     */
    public static void writeToFile(InputStream is, File file) throws IOException {
        FileOutputStream fs = null;
        try {
            fs = new FileOutputStream(file);
            byte[] buffer = new byte[1024];
            int byteread = 0;
            while ((byteread = is.read(buffer)) != -1) {
                fs.write(buffer, 0, byteread);
            }
        } catch (IOException e) {
            throw e;
        } finally {
            if (fs != null) {
                fs.close();
            }
            is.close();
        }
    }

    /**
     * 删除文件夹(会删除文件夹下所有的文件)
     *
     * @param dir
     * @return
     */
    public static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            //递归删除目录中的子目录下
            for (int i = 0; i < children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }
        // 目录此时为空,可以删除
        return dir.delete();
    }

    public static File createDir(String dirPath) {
        File dir = new File(dirPath);
        //如果文件夹不存在
        if (!dir.exists()) {
            //创建文件夹
            dir.mkdir();
        }
        return dir;
    }

    public static void zipDir(String directoryName, ZipOutputStream zos, BufferedOutputStream bos) {
        File file = new File(directoryName);
        if (file.exists()) {
            File[] fileList = file.listFiles();
            assert fileList != null;
            for (File f : fileList) {
                // 压缩单个文件到 zos
                String zipName = f.getName();
                try {
                    zos.putNextEntry(new ZipEntry(zipName));
                    int len;
                    FileInputStream is = new FileInputStream(f);
                    BufferedInputStream bis = new BufferedInputStream(is);
                    byte[] bytes = new byte[1024];
                    while ((len = bis.read(bytes)) != -1) {
                        bos.write(bytes, 0, len);

                    }
                    bos.flush();
                    zos.flush();

//                    结束当前压缩文件的添加
                    bis.close();
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();

                }

            }

        }
    }

    /**
     * 路径拼接工具方法
     * @param filePath 文件路径
     * @return 拼接结果
     */
    public static String filePathJoin(String... filePath) {
        return StringUtils.join(filePath, File.separator);
    }

}
package com.ly.cloud.utils.exportUtil;

import cn.afterturn.easypoi.excel.ExcelXorHtmlUtil;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;

public class VelocityTemplateExporter extends AbsExporter {

    private static final Logger LOGGER = LoggerFactory.getLogger(VelocityTemplateExporter.class);

    private String templateFilename;

    public VelocityTemplateExporter(String templateFilename) {
        this.templateFilename = templateFilename;
    }

    @Override
    public String getTargetFileSuffix() {
        return ".xlsx";
    }

    @Override
    public Writable render(Map<String, Object> dataSource) {
        String html = VelocityUtils.render(this.templateFilename + ".vm", dataSource);
        LOGGER.trace("渲染的html为:\n{}", html);
        Workbook workbook = ExcelXorHtmlUtil.htmlToExcel(html, ExcelType.XSSF);
        if (null == workbook) {
            throw new NullPointerException("workbook 为 null");
        }
        return new WorkbookWrapper(workbook);
    }
}
package com.ly.cloud.utils.exportUtil;

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.VelocityException;

import java.io.StringWriter;
import java.util.Map;
import java.util.Properties;

public class VelocityUtils {

    /**
     * 模板文件所在目录
     */
    private static final String TEMPLATE_FILE_DIR = FileUtils.filePathJoin("file", "velocityTemp");

    static {
        //初始化参数
        Properties properties = new Properties();
        //设置 velocity 资源加载方式为 class
        properties.setProperty("resource.loader", "class");
        //设置 velocity 资源加载方式为 class 时的处理类
        properties.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        // 执行初始化
        Velocity.init(properties);
    }

    /**
     * 渲染对应模板,并输出渲染结果
     * @param templateFileName 模板文件名
     * @param velocityContext 上下文对象,即渲染使用的数据源
     * @return 渲染结果
     */
    public static String render(String templateFileName, VelocityContext velocityContext) throws VelocityException {
        StringWriter writer = new StringWriter();
        Velocity.mergeTemplate(FileUtils.filePathJoin(TEMPLATE_FILE_DIR, templateFileName), "UTF-8", velocityContext, writer);
        return writer.toString();
    }

    /**
     * 渲染对应模板,并输出渲染结果
     * @param templateFileName 模板文件名
     * @param renderDataSource 渲染使用的数据源
     * @return 渲染结果
     */
    public static String render(String templateFileName, Map<String, Object> renderDataSource) throws VelocityException {
        VelocityContext velocityContext = new VelocityContext(renderDataSource);
        return render(templateFileName, velocityContext);
    }
}
package com.ly.cloud.utils.exportUtil;

import org.apache.poi.ss.usermodel.Workbook;

import java.io.IOException;
import java.io.OutputStream;

public class WorkbookWrapper implements Writable {

    private Workbook workbook;

    public WorkbookWrapper(Workbook workbook) {
        this.workbook = workbook;
    }

    @Override
    public void write(OutputStream outputStream) throws IOException {
        this.workbook.write(outputStream);
    }
}
package com.ly.cloud.utils.exportUtil;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import org.apache.poi.ss.usermodel.Workbook;

import java.util.Map;
import java.util.function.Function;

public class ExcelTemplateExporter extends AbsExporter {

    private TemplateExportParams templateExportParams;

    private Function<Workbook, Workbook> afterRender;

    public ExcelTemplateExporter(String templateFilename) {
        this(templateFilename, null);
    }

    public ExcelTemplateExporter(String templateFilename, Function<Workbook, Workbook> afterRender) {
        this.templateExportParams = new TemplateExportParams("file/excelTemp/" + templateFilename + ".xlsx");
        this.afterRender = afterRender;
    }

    @Override
    public Writable render(Map<String, Object> dataSource) {
        Workbook workbook = ExcelExportUtil.exportExcel(this.templateExportParams, dataSource);
        if (null == workbook) {
            throw new NullPointerException("workbook 为 null");
        }
        if (this.afterRender != null) {
            workbook = this.afterRender.apply(workbook);
        }
        return new WorkbookWrapper(workbook);
    }

    @Override
    public String getTargetFileSuffix() {
        return ".xlsx";
    }
}
package com.lili.exportUtil;

import org.apache.poi.xwpf.usermodel.XWPFDocument;

import java.io.IOException;
import java.io.OutputStream;

public class XWPFDocumentWrapper implements Writable {

    private XWPFDocument xwpfDocument;

    public XWPFDocumentWrapper(XWPFDocument xwpfDocument) {
        this.xwpfDocument = xwpfDocument;
    }

    @Override
    public void write(OutputStream outputStream) throws IOException {
        this.xwpfDocument.write(outputStream);
    }
}
package com.lili.exportUtil;

import cn.afterturn.easypoi.word.WordExportUtil;

import org.apache.poi.xwpf.usermodel.XWPFDocument;

import java.util.Map;

public class WordTemplateExporter extends AbsExporter {

    private String templateFilePath;

    public WordTemplateExporter(String templateFilename) {
        this.templateFilePath = "file/excelTemp/" + templateFilename + ".docx";
    }

    @Override
    public String getTargetFileSuffix() {
        return ".docx";
    }

    @Override
    public Writable render(Map<String, Object> dataSource) throws Exception {
        XWPFDocument xwpfDocument = WordExportUtil.exportWord07(templateFilePath, dataSource);
        XWPFDocumentWrapper xwpfDocumentWrapper = new XWPFDocumentWrapper(xwpfDocument);
        return xwpfDocumentWrapper;
    }
}

2、引入所需依赖

        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-web</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>eu.bitwalker</groupId>
            <artifactId>UserAgentUtils</artifactId>
            <version>1.21</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>
        <dependency>
			<groupId>org.jsoup</groupId>
			<artifactId>jsoup</artifactId>
			<version>1.12.1</version>
		</dependency>

3、导出案例

3.1、导出以下excel

方式一:

操作步骤:

放的位置要和工具类设置位置一样即可。

然后编写下载接口即可完成。

访问接口即可下载成功,如下

方式二:

操作步骤:

新建vm文件。放入合适位置(位置取自工具类,可以自行调整)

vm文件内容示例:

## 各个列的样式,主要是加上边框
#set($style = 'style="border: 1; height:50;width:12"')
#set($height = 'style="height:30;font-size:9"')
#set($fontSize = 'style="font-size: 20;"')
## 方法,如果存在则显示否则显示空
#macro(displayValue $value)
    #if($value)
        $value
    #else
    #end
#end

## sheetName 必须存在
<table sheetName="心理咨询来访者登记表">
    <tr>
        <th colspan="6" $fontSize>心理咨询来访者登记表</th>
    </tr>
    <tr>
        <th $height colspan="6">为使咨询更有效率,节约你的时间,希望朋友们在咨询前能详细提供如下资料,
            我们承诺进行严格保密!</th>
    </tr>
    <tr>
        <th $style>姓名</th>
        <th $style>#displayValue($xm)</th>
        <th $style>性别</th>
        <th $style>#displayValue($xb)</th>
        <th $style>年龄</th>
        <th $style>#displayValue($age)</th>
    </tr>
    <tr>
        <th $style>院系</th>
        <th $style>#displayValue($yx)</th>
        <th $style>班级</th>
        <th $style>#displayValue($bj)</th>
        <th $style>寝室</th>
        <th $style>#displayValue($qs)</th>
    </tr>
    <tr>
        <th $style>联系电话</th>
        <th $style>#displayValue($lxfs)</th>
        <th colspan="2" $style>辅导员/班主任</th>
        <th colspan="2" $style>#displayValue($fdyxm)</th>
    </tr>
    <tr>
        <th colspan="3" $style>如果您有紧急情况,希望我们与谁联系</th>
        <th colspan="3" $style>#displayValue($jjlxr)</th>
    </tr>
    <tr>
        <th $style>
            家庭情况(请简要介绍您的家庭背景和成长经历)
        </th>
        <th $style colspan="5">#displayValue($jtqk)</th>
    </tr>
    <tr>
        <th $style>
            咨询问题(你困惑或难以摆脱的问题是什么?)
        </th>
        <th $style colspan="5">#displayValue($zxwt)</th>
    </tr>
    <tr>
        <th $style>
            你期望达到的咨询目的
        </th>
        <th $style colspan="5">#displayValue($zxmd)</th>
    </tr>
    <tr>
        <th colspan="2" $style>以前是否接受过心理咨询或治疗</th>
        <th  $style>#displayValue($sfkgys)</th>
        <th colspan="2" $style>是否做过相关的心理测验</th>
        <th  $style>#displayValue($fxlcy)</th>
    </tr>
    <tr>
        <th colspan="2" $style>若做过,结果是:</th>
        <th colspan="4" $style>#displayValue($xlcyjg)</th>
    </tr>
    <tr>
        <th colspan="2" $style>预约咨询时间(至少需提前三天预约)</th>
        <th colspan="4" $style>#displayValue($yysj) #displayValue($yysd)</th>
    </tr>
</table>

这里无非是自己将页面通过tr,th并且合并单元格的方式画出来。

使用方法:

结果如下:

总结:

方式一:适合那些单条数据,没有变化的excel,直接制作好excel文件,放入项目里面即可完成

方式二:vm模板可以定义方法与变量,比较适合动态导出sql,可以根据不同map的结果导出不同样子的excel。

3.2、导出以下excel

这种就很常见了,平常简单的导出都是这个样子的。今天就用这个工具类来实现一下

方式一:还是直接放入项目里面的方式

编写接口进行下载测试:

下载结果如下:

注意:这里开头要加{{fe:maplist  结尾要加}},其余就是t.变量名字。其中maplist跟接口设置数据的key对应即可

方式二:采用vm的方式

这个时候就用到foreach循环了,vm文件如下

然后修改接口的实现类

结果如下:

总结:

两种方式都很不错,可以实现效果,但是前者针对那种一成不变的或者简单的很方便。后者对于那种内部循环的就很见效了,比如下面这个。

红圈的部分就是需要根据次数来循环的部分,这个时候就非常适合用vm模板的方式了。

拓展

这个vm文件可自行扩展。非常灵活。下面是我用过的几个vm实战例子

## 二级列(等级)标题 list
#set($subColumnTitleList = [])
## 数据索引 list
#set($dataIndexList = [])
## 各个列的样式,主要是加上边框
#set($style = 'style="border: 1"')
#set($styleW = 'style="border: 1 solid black; width: 25;"')
#set($fontSize = 'style="border: 1 solid black; font-size: 20;"')
#set($bgck = 'style="border: 1 solid black; background-color: yellow;"')
#set($colspanValue = $lbmc.size() + 1)
## sheetName 必须存在
<table sheetName="columnsExportTest">
    <tr>
        <th colspan="$num" $fontSize>$title</th>
    </tr>
    <tr>
        <th colspan="$num" $style style="font-weight: bold">单位:XXX技术学院</th>
    </tr>
    <tr>
        <th rowspan="2" $style>院系</th>
        <th colspan="$colspanValue" $style>学校总人数</th>
        #foreach($column in $columns)
            #if($column.children)
##                <th colspan="$column.children.size()" $style>$column.title</th>
                #foreach($subColumn in $column.children)
                    <th rowspan="1" colspan="2" $style>$subColumn.title</th>
                    ## 因为 add 方法有返回值,所以这里需要用 html 的方式注释一下,下面同理
                    <!--$subColumnTitleList.add("人数")-->
                    <!--$subColumnTitleList.add("金额")-->
                    <!--$dataIndexList.add($subColumn.dataIndex)-->
                    <!--$dataIndexList.add($subColumn.dataIndexTemp)-->
                #end
            #else
                <th rowspan="1" colspan="2" $style>$column.title</th>
                <!--$subColumnTitleList.add("人数")-->
                <!--$subColumnTitleList.add("金额")-->
                <!--$dataIndexList.add($column.dataIndex)-->
                <!--$dataIndexList.add($column.dataIndexTemp)-->
            #end
        #end
        <th colspan="2" $style>合计</th>
    </tr>
    <tr>
        #foreach($mc in $lbmc)
            <th $style>$mc</th>
        #end
        <th $style>合计</th>
        #if($subColumnTitleList)
            #set($count = 1)
            #foreach($subColumnTitle in $subColumnTitleList)
                #if ($count % 2 == 0)
                    <th $bgck>$subColumnTitle</th>
                #else
                    <th $style>$subColumnTitle</th>
                #end
                #set($count = $count + 1)
            #end
        #end
        <th $style>人数</th>
        <th $bgck>发放金额</th>
    </tr>
    #foreach($record in $dataSource)
        <tr>
            <td $styleW>$record.BMMC</td>
            #set($sum = 0)
            #foreach($dm in $lbdm)
                <td $style>$record.get($dm)</td>
                #set($currentValue = $record.get($dm))  ## 获取当前值
                #set($sum = $sum + $currentValue)  ## 累加值到 $sum 变量
            #end
            <td $style>$sum</td>
            #set($count = 1)
            #foreach($dataIndex in $dataIndexList)
                #if($count % 2 == 0)
                    <td $bgck>$record.get($dataIndex)</td>
                #else
                    <td $style>$record.get($dataIndex)</td>
                #end
                #set($count = $count + 1)
            #end
            #if($record.BMMC == "合计")
                <td colspan="1" $style>$record.HJRC</td>
                <td colspan="1" $bgck>$record.HJJE</td>
            #else
                <td $style>$record.HJRC</td>
                <td $bgck>$record.HJJE</td>
            #end
        </tr>
    #end
</table>
## 各个列的样式,主要是加上边框
#set($style = 'style="border: 1; height:50;width:18"')
#set($bold = 'style="border: 1; height:50;width:18;font-weight: bold;"')
#set($fontSize = 'style="font-size: 20;"')
#macro(displayValue $value)
    #if($value)
        $value
    #else
    #end
#end
## sheetName 必须存在
<table sheetName="心理咨询个案记录表">
    <tr>
        <th colspan="6" $fontSize>心理咨询个案记录表</th>
    </tr>
    <tr>
        <th $style>咨询师</th>
        <th $style>#displayValue($ZXS)</th>
        <th $style>咨询次数</th>
        <th $style>#displayValue($dataSource.size())</th>
        <th $style>来访日期</th>
        <th $style>#displayValue($dataSource.get(0).ZXSJ)</th>
    </tr>
    <tr>
        <th $style>来访者</th>
        <th $style>#displayValue($LFZ)</th>
        <th $style>性别</th>
        <th $style>#displayValue($XB)</th>
        <th $style>年龄</th>
        <th $style>#displayValue($AGE)</th>
    </tr>
    <tr>
        <th $style>系部班级</th>
        <th $style>#displayValue($XBBJ)</th>
        <th $style>辅导员/班主任</th>
        <th $style>#displayValue($FDYXM)</th>
        <th $style>联系人及联系方式</th>
        <th $style>#displayValue($JJLXR)</th>
    </tr>
    #foreach($record in $dataSource)
        <tr>
            <th  rowspan="11" $bold>#displayValue($record.title)</th>
            <th $bold colspan="5">表现出的问题</th>
        </tr>
        <tr>
            <th $style>来访者自述</th>
            <th colspan="4" $style>#displayValue($record.LFZZS)</th>
        </tr>
        <tr>
            <th $bold colspan="5">问题原因</th>
        </tr>
        <tr>
            <th $style>促使因素</th>
            <th colspan="4" $style>#displayValue($record.CSYS)</th>
        </tr>
        <tr>
            <th $style>先前因素</th>
            <th colspan="4" $style>#displayValue($record.XQYS)</th>
        </tr>
        <tr>
            <th $style>社会因素</th>
            <th colspan="4" $style>#displayValue($record.SHYS)</th>
        </tr>
        <tr>
            <th $style>健康状况及治疗史</th>
            <th colspan="4" $style>#displayValue($record.JKZTJZLS)</th>
        </tr>
        <tr>
            <th $bold colspan="5">分析、评估与咨询方案</th>
        </tr>
        <tr>
            <th $style>评估诊断</th>
            <th colspan="4" $style>#displayValue($record.PGZD)</th>
        </tr>
        <tr>
            <th $style>咨询目标</th>
            <th colspan="4" $style>#displayValue($record.ZXMB)</th>
        </tr>
        <tr>
            <th $style>咨询方法</th>
            <th colspan="4" $style>#displayValue($record.ZXFF)</th>
        </tr>
    #end
</table>

3.3、导出word

接口:

结果:

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

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

相关文章

AI学习笔记之六:无监督学习如何帮助人类挖掘数据金矿和防范网络欺诈

在这个大数据时代&#xff0c;企业和组织在过去几十上百年的经营过程中积累了大量的原始数据&#xff0c;其中蕴含着宝贵的商业价值和见解。然而&#xff0c;要从这些海量的、未经标记和处理的数据中发现隐藏的规律和知识&#xff0c;并不是一件容易的事情。这就好比要从一座巨…

测试用例的设计(1)

目录 1. 测试用例的基本要素 2.测试用例的设计方法 2.1.基于需求设计 2.2根据功能需求测试 2.3非功能测试 3. 具体的设计方法 3.1等价类法 3.2边界值法 3.3判定表 1. 测试用例的基本要素 测试用例是为了实施测试而面向测试的系统提供的一组集合,这组集合包含:测试环境,…

使用FFmpeg源码配置程序configure查看所有支持的编码器/解码器/封装/解封装及网络协议

查看支持编码器: configure --list-encoders 查看支持编码器: configure --list-decoders 查看所有支持的封装: configure --list-muxers 查看所有支持的解封装: configure --list-demuxers 查看所有支持的网络通信协议: configure --list-protocols

数据预处理|数据清洗|使用Pandas进行异常值清洗

数据预处理|数据清洗|使用Pandas进行异常值清洗 使用Pandas进行异常值清洗1. 异常值检测1.1 简单统计分析1.2 散点图方法1.3 3σ原则1.4 箱线图 2. 异常值处理2.1 直接删除2.2 视为缺失值2.3 平均值修正2.4 盖帽法2.5 分箱平滑法2.6 回归插补2.7 多重插补2.8 不处理 使用Pandas…

通过Rothko罗斯科绘画学习CSS盒子

本文章属于学习笔记&#xff0c;在https://www.freecodecamp.org/chinese/learn/2022/responsive-web-design/中练习 1、使用 padding 简写属性来增加两个元素之间的空间到。 .canvas {} .frame { padding:50px; }2、overflow 设置为 hidden - 将画布更改回其原始尺寸。overfl…

193基于matlab的基于两轮驱动机器人的自适应轨迹跟踪算法

基于matlab的基于两轮驱动机器人的自适应轨迹跟踪算法&#xff0c;将被跟踪轨迹分段作为跟踪直线处理&#xff0c;相邻离散点之间为一段新的被跟踪轨迹。程序已调通&#xff0c;可直接运行。 193 自适应轨迹跟踪算法 两轮驱动机器人 - 小红书 (xiaohongshu.com)

matplotlib如何设置中文为宋体,英文为新罗马Times New Roman

问题描述 论文附图通常需要将中文设置为宋体&#xff0c;英文设置为新罗马字体&#xff08;Times New Roman&#xff09;。matplotlib中可以这样设置字体&#xff1a; plt.rcParams[font.sans-serif] [SimSun] plt.rcParams[font.sans-serif] [Times New Roman]但是这样设置…

<DFS剪枝>数字王国之军训排队

其实就是将搜索过程一些不必要的部分直接剔除掉。 剪枝是回溯法的一种重要优化手段&#xff0c;往往需要先写一个暴力搜索&#xff0c;然后找到某些特殊的数学关系&#xff0c;或者逻辑关系&#xff0c;通过它们的约>束让搜索树尽可能浅而小&#xff0c;从而达到降低时间复杂…

绪论——算法设计原则【数据科学与工程算法基础】

一、题记 最近情绪不太稳定&#xff0c;些许烦躁&#xff0c;也就一直没践行前边说的“学习记录”的想法。现在开始做了&#xff0c;春华易逝&#xff0c;正当时&#xff0c;有想法就去做&#xff0c;踌躇懊悔是这个年纪最不该做的事。 二、前言 之前说了分块做这个系列&#x…

动态规划:4种遍历方向图解+Python实现

前言 动态规划类题型在遍历过程中&#xff0c;根据状态转移函数的不同&#xff0c;代码实现时遍历的方向也会有所差异。总的来说&#xff0c;一共可以总结为下图四种模式&#xff1a; 红色五角星表示当前要计算的状态值&#xff1b;白底箭头代表哪些状态要提前算出来&#xf…

VSCode ARM CortexM 开发

VSCode ARM CortexM 开发: http://coffeelatte.vip.cpolar.top/post/software/applications/vscode/vscode_arm_cortexm_开发/ 文章目录 VSCode ARM CortexM 开发: <http://coffeelatte.vip.cpolar.top/post/software/applications/vscode/vscode_arm_cortexm_%E5%BC%80%E5%…

三星计划将其NAND闪存芯片价格上调最高20%

韩国媒体一份报告显示&#xff0c;三星电子的内存业务成功挺过了去年的市场低迷时期。最近&#xff0c;其减产策略终于见效&#xff0c;芯片价格随之上升。 据报导&#xff0c;今年第一季度&#xff0c;三星计划将其NAND闪存芯片价格上调最高20%&#xff0c;目标是恢复其内存芯…

【析】一类动态车辆路径问题模型和两阶段算法

一类动态车辆路径问题模型和两阶段算法 摘要 针对一类动态车辆路径问题&#xff0c;分析4种主要类型动态信息对传统车辆路径问题的本质影响&#xff0c;将动态车辆路径问题(Dynamic Vehicle Routing Problem, DVRP)转化为多个静态的多车型开放式车辆路径问题(The Fleet Size a…

Java基础-复制

复制 前言引用拷贝浅拷贝深拷贝重写clone()方法序列化 前言 在编码中,我们可能会遇到需要将对象的属性复制到另一个对象中,这种情况叫做拷贝. 拷贝与Java内存结构有密切关系,拷贝有三种情况,引用拷贝,深拷贝和浅拷贝,下面来了解一下. 引用拷贝 引用拷贝会生成一个新的对象引…

微信小程序基础面试题

1、简述微信小程序原理 小程序本质就是一个单页面应用&#xff0c;所有的页面渲染和事件处理&#xff0c;都在一个页面内进行&#xff0c;但又可以通过微信客户端调用原生的各种接口&#xff1b;它的架构&#xff0c;是数据驱动的架构模式&#xff0c;它的UI和数据是分离的&am…

Windows上Git LFS的安装和使用

到Git LFS官网下载 传送门 初始化GitHub LFS和Git仓库 在仓库目录中运行&#xff1a; git lfs install再运行&#xff1a; git init跟踪大文件 git lfs track "*.zip"添加并提交文件 git add . git commit -m "Add large files"上传到我的github 配…

3.15作业

什么是IP地址&#xff1a;IP地址的作用是在网络中唯一标识和定位设备 IP地址和MAC地址的区别&#xff1a;IP地址是逻辑地址&#xff0c;网络层标识设备&#xff0c;可以更改&#xff0c;是全球互联网的唯 一 标识 MAC地址是物…

【CTF笔记】 CTF web方向笔记分享 免费 附预览图

个人不怎么记东西&#xff0c;笔记不多&#xff0c;师傅们凑合看… 百度网盘&#xff1a;https://pan.baidu.com/s/1PspihUX28Y_AOQZPurHqKA 麻烦各位师傅帮忙填写一下问卷&#xff0c;提取码在问卷填写结束后显示~ 【https://www.wjx.cn/vm/mBBTTKm.aspx# 】 &#xff08;…

大型政企寻求“智能化配方”,谁是“偏方”,谁是“验方”?

文 | 智能相对论 作者 | 叶远风 两会落幕&#xff0c;“人工智能”已成为国策&#xff0c;而全面推进智能化建设&#xff0c;大型政企首当其冲、责无旁贷——它们既是智能化转型升级的重要构成部分&#xff0c;也能直接在垂直领域形成价值引领、以点带片。 当智能成为大型政…

某鱼弹幕逆向

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01;wx a15018…