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

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) {// 压缩单个文件到 zosString 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;}@Overridepublic String getTargetFileSuffix() {return ".xlsx";}@Overridepublic 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 资源加载方式为 classproperties.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;}@Overridepublic 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;}@Overridepublic 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);}@Overridepublic 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;}@Overridepublic 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";}@Overridepublic String getTargetFileSuffix() {return ".docx";}@Overridepublic 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.mzph.cn/news/747794.shtml

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

相关文章

Unity中的网格创建和曲线变形

Unity中的网格创建和曲线变形 3D贝塞尔曲线变形贝塞尔曲线基础线性公式二次方公式三次方公式 Unity 实现3D贝塞尔曲线变形准备工作脚本概述变量定义 变量解析函数解析 获取所有子节点GetAllChildren 获取所有子节点UpdateBezierBend 控制点更新CalculateBezier Bezier 曲线公式…

【算法杂货铺】二分算法

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 朴素二分查找 &#x1f4c2; 朴素二分模板 &#x1f4c1; 查找区间端点处 细节&#xff08;重要&#xff09; &#x1f4c2; 区间左端点处模板 &#x1f4c2; 区间右端点处模板 &#x1f4c1; 习题 1. 35. 搜索插入位…

phpcms头像上传漏洞引发的故事

目录 关键代码 第一次防御 第一次绕过 第二次防御 第二次绕过 第三次防御 第三次绕过 如何构造一个出错的压缩包 第四次防御 第四次绕过 本篇文章是参考某位大佬与开发人员对于文件包含漏洞的较量记录下的故事&#xff0c;因为要学习文件包含漏洞&#xff0c;就将大佬…

什么是 HTTPS?它是如何解决安全性问题的?

什么是 HTTPS&#xff1f; HTTPS&#xff08;HyperText Transfer Protocol Secure&#xff09;是一种安全的通信协议&#xff0c;用于在计算机网络上安全地传输超文本&#xff08;如网页、图像、视频等&#xff09;和其他数据。它是 HTTP 协议的安全版本&#xff0c;通过使用加…

Java开发从入门到精通(九):Java的面向对象OOP:成员变量、成员方法、类变量、类方法、代码块、单例设计模式

Java大数据开发和安全开发 &#xff08;一)Java的变量和方法1.1 成员变量1.2 成员方法1.3 static关键字1.3.1 static修饰成员变量1.3.1 static修饰成员变量的应用场景1.3.1 static修饰成员方法1.3.1 static修饰成员方法的应用场景1.3.1 static的注意事项1.3.1 static的应用知识…

微服务技术栈SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式(五):分布式搜索 ES-中

文章目录 一、DSL查询文档1.1 简单查询1.2 复合查询 二、搜索结果处理三、RestClient演示 查询与结果分析四、案例4.1 问题解析4.2 代码4.2.1 实体bean4.2.2 控制层4.2.3 业务service4.2.4 启动类 一、DSL查询文档 1.1 简单查询 # 1. DSL查询 # 1.1 查询所有GET /hotel/_searc…

JavaScript 进阶(一)

一、作用域 作用域&#xff08;scope&#xff09;规定了变量能够被访问的“范围”&#xff0c;离开了这个“范围”变量便不能被访问。 作用域分为&#xff1a; 局部作用域 、全局作用域。 1.1局部作用域 局部作用域分为函数作用域和块作用域。 1. 函数作用域&#xff1a; 在函数…

分布式系统常见负载均衡实现模式

分布式系统常见负载均衡实现模式 1. 4层负载均衡1.1. 负载均衡的常见需求1.2. 负载均衡的实现模式1.2.1 DR模式1.2.2 TUN模式1.2.3 NAT模式1.2.4 FULLNAT模式1.2.5 4种模式的差异 1.3. 负载均衡的均衡算法1.3.1 静态负载均衡1.3.2 轮询法1.3.3 加权循环法1.3.4 IP 哈希法1.3.5 …

Vue前端开发记录(一)

本篇文章中的图片均为深色背景&#xff0c;请于深色模式下观看 说明&#xff1a;本篇文章的内容为vue前端的开发记录&#xff0c;作者在这方面的底蕴有限&#xff0c;所以仅作为参考 文章目录 一、安装配置nodejs,vue二、vue项目目录结构三、前期注意事项0、组件1、数不清的报…

首个ChatGPT机器人- Figure 01;李开复旗下零一万物推出Yi系列AI大模型API

&#x1f989; AI新闻 &#x1f680; 首个ChatGPT机器人- Figure 01 摘要&#xff1a;Figure 01是一个由初创公司Figure联合OpenAI开发的人形机器人。它展示了与人类和环境互动的能力&#xff0c;可以说话、看东西&#xff0c;并且可以执行各种任务&#xff0c;如递食物、捡垃…

QT中dumpcpp以及dumpdoc使用

qt中调用COM的方式方法有四种&#xff0c;参考解释在 Qt 中使用 ActiveX 控件和 COM (runebook.dev) 介绍dumpcpp的使用方法Qt - dumpcpp 工具 (ActiveQt) (runebook.dev)&#xff1a; 在安装好了的qt电脑上&#xff0c;通过powershell窗口来实现&#xff0c;powershell比cmd要…

算法的基本概念和复杂度

目录 一. 算法的基本概念1.1 什么是算法1.2 算法的五个特性1.3 怎么才算好的算法 二. 算法的时间复杂度三. 算法的空间复杂度 \quad 一. 算法的基本概念 \quad \quad 1.1 什么是算法 算法可以用自然语言来描述, 也可以用伪代码和代码来描述 \quad 1.2 算法的五个特性 有穷性, 一…

ASP.NET-Server.HtmlEncode

目录 背景: 1.转义特殊字符&#xff1a; 2.防止跨站脚本攻击&#xff08;XSS&#xff09;&#xff1a; 3.确保输出安全性&#xff1a; 4.保留原始文本形式&#xff1a; 5.与用户输入交互安全&#xff1a; 实例说明: 不用Server.HtmlEncode 效果展示: 用Server.HtmlEnc…

SpringMVC重点记录

目录 1.学习重点2.回顾MVC3.回顾servlet4.初始SpringMVC4.1.为什么要学SpringMVC?4.2.SpringMVC的中重点DispatcherServlet4.3.SpringMVC项目的搭建4.4.MVC框架要做哪些事情?4.5.可能会遇到的问题 5.SpringMVC的执行原理6.使用注解开发SpringMVC7.Controller控制总结8.RestF…

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%…

鸿蒙实战开发:【分布式软总线组件】

简介 现实中多设备间通信方式多种多样(WIFI、蓝牙等)&#xff0c;不同的通信方式使用差异大&#xff0c;导致通信问题多&#xff1b;同时还面临设备间通信链路的融合共享和冲突无法处理等挑战。分布式软总线实现近场设备间统一的分布式通信管理能力&#xff0c;提供不区分链路…

音频占用磁盘空间太多 需要把mp3音频转aac音频缩小占用空间 应该怎么操作?

一&#xff1a;什么是aac格式&#xff1f; aac是一种音频压缩格式&#xff0c;它是MPEG-2标准下的一种音频压缩方式&#xff0c;也可以作为HE-AAC&#xff0c;AAC或AAC-LC格式使用&#xff0c;是音频压缩领域中的一种重要格式。与MP3的比较&#xff0c;aac在保证音质的同时可以…

K8S CNI

OCI概念 OCI&#xff0c;Open Container Initiative&#xff0c;开放容器标准&#xff0c;是一个轻量级&#xff0c;开放的治理结构&#xff08;项目&#xff09;&#xff0c;在 Linux 基金会的支持下成立&#xff0c;致力于围绕容器格式和运行时创建开放的行业标准。 OCI 项目…

stm32-定时器输入捕获

目录 一、输入捕获简介 二、输入捕获框图 1.定时器总框图 2.输入捕获框图 3.主从触发模式 三、固件库实现 1.定时器测量PWM频率 2.PWMI模式 一、输入捕获简介 二、输入捕获框图 1.定时器总框图 上图可知&#xff0c;四个输入捕获和输出比较共用4个CCR寄存器&#x…

Android SystemServer进程解析

SystemServer进程在android系统中占了举足轻重的地位&#xff0c;系统的所有服务和SystemUI都是由它启动。 一、SystemServer进程主函数流程 1、主函数三部曲 //frameworks/base/services/java/com/android/server/SystemServer.java /** * The main entry point from zy…