diff --git a/os-common/src/main/java/com/os/common/annotation/Excel.java b/os-common/src/main/java/com/os/common/annotation/Excel.java index dd6be88..0401309 100644 --- a/os-common/src/main/java/com/os/common/annotation/Excel.java +++ b/os-common/src/main/java/com/os/common/annotation/Excel.java @@ -11,7 +11,7 @@ import com.os.common.utils.poi.ExcelHandlerAdapter; /** * 自定义导出Excel数据注解 - * + * * @author ruoyi */ @Retention(RetentionPolicy.RUNTIME) @@ -83,11 +83,21 @@ public @interface Excel */ public String prompt() default ""; + /** + * 是否允许内容换行 + */ + public boolean wrapText() default false; + /** * 设置只能选择不能输入的列内容. */ public String[] combo() default {}; + /** + * 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解. + */ + public boolean comboReadDict() default false; + /** * 是否需要纵向合并单元格,应对需求:含有list集合单元格) */ diff --git a/os-common/src/main/java/com/os/common/utils/DictUtils.java b/os-common/src/main/java/com/os/common/utils/DictUtils.java index a1d974c..499c5e7 100644 --- a/os-common/src/main/java/com/os/common/utils/DictUtils.java +++ b/os-common/src/main/java/com/os/common/utils/DictUtils.java @@ -3,6 +3,7 @@ package com.os.common.utils; import java.util.Collection; import java.util.List; import com.alibaba.fastjson2.JSONArray; +import com.os.common.utils.StringUtils; import com.os.common.constant.CacheConstants; import com.os.common.core.domain.entity.SysDictData; import com.os.common.core.redis.RedisCache; @@ -10,7 +11,7 @@ import com.os.common.utils.spring.SpringUtils; /** * 字典工具类 - * + * * @author ruoyi */ public class DictUtils @@ -22,7 +23,7 @@ public class DictUtils /** * 设置字典缓存 - * + * * @param key 参数键 * @param dictDatas 字典数据列表 */ @@ -33,7 +34,7 @@ public class DictUtils /** * 获取字典缓存 - * + * * @param key 参数键 * @return dictDatas 字典数据列表 */ @@ -49,31 +50,39 @@ public class DictUtils /** * 根据字典类型和字典值获取字典标签 - * + * * @param dictType 字典类型 * @param dictValue 字典值 * @return 字典标签 */ public static String getDictLabel(String dictType, String dictValue) { + if (StringUtils.isEmpty(dictValue)) + { + return StringUtils.EMPTY; + } return getDictLabel(dictType, dictValue, SEPARATOR); } /** * 根据字典类型和字典标签获取字典值 - * + * * @param dictType 字典类型 * @param dictLabel 字典标签 * @return 字典值 */ public static String getDictValue(String dictType, String dictLabel) { + if (StringUtils.isEmpty(dictLabel)) + { + return StringUtils.EMPTY; + } return getDictValue(dictType, dictLabel, SEPARATOR); } /** * 根据字典类型和字典值获取字典标签 - * + * * @param dictType 字典类型 * @param dictValue 字典值 * @param separator 分隔符 @@ -83,31 +92,31 @@ public class DictUtils { StringBuilder propertyString = new StringBuilder(); List datas = getDictCache(dictType); - - if (StringUtils.isNotNull(datas)) + if (StringUtils.isNull(datas)) { - if (StringUtils.containsAny(separator, dictValue)) + return StringUtils.EMPTY; + } + if (StringUtils.containsAny(separator, dictValue)) + { + for (SysDictData dict : datas) { - for (SysDictData dict : datas) + for (String value : dictValue.split(separator)) { - for (String value : dictValue.split(separator)) + if (value.equals(dict.getDictValue())) { - if (value.equals(dict.getDictValue())) - { - propertyString.append(dict.getDictLabel()).append(separator); - break; - } + propertyString.append(dict.getDictLabel()).append(separator); + break; } } } - else + } + else + { + for (SysDictData dict : datas) { - for (SysDictData dict : datas) + if (dictValue.equals(dict.getDictValue())) { - if (dictValue.equals(dict.getDictValue())) - { - return dict.getDictLabel(); - } + return dict.getDictLabel(); } } } @@ -116,7 +125,7 @@ public class DictUtils /** * 根据字典类型和字典标签获取字典值 - * + * * @param dictType 字典类型 * @param dictLabel 字典标签 * @param separator 分隔符 @@ -126,8 +135,11 @@ public class DictUtils { StringBuilder propertyString = new StringBuilder(); List datas = getDictCache(dictType); - - if (StringUtils.containsAny(separator, dictLabel) && StringUtils.isNotEmpty(datas)) + if (StringUtils.isNull(datas)) + { + return StringUtils.EMPTY; + } + if (StringUtils.containsAny(separator, dictLabel)) { for (SysDictData dict : datas) { @@ -154,9 +166,51 @@ public class DictUtils return StringUtils.stripEnd(propertyString.toString(), separator); } + /** + * 根据字典类型获取字典所有值 + * + * @param dictType 字典类型 + * @return 字典值 + */ + public static String getDictValues(String dictType) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + if (StringUtils.isNull(datas)) + { + return StringUtils.EMPTY; + } + for (SysDictData dict : datas) + { + propertyString.append(dict.getDictValue()).append(SEPARATOR); + } + return StringUtils.stripEnd(propertyString.toString(), SEPARATOR); + } + + /** + * 根据字典类型获取字典所有标签 + * + * @param dictType 字典类型 + * @return 字典值 + */ + public static String getDictLabels(String dictType) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + if (StringUtils.isNull(datas)) + { + return StringUtils.EMPTY; + } + for (SysDictData dict : datas) + { + propertyString.append(dict.getDictLabel()).append(SEPARATOR); + } + return StringUtils.stripEnd(propertyString.toString(), SEPARATOR); + } + /** * 删除指定字典缓存 - * + * * @param key 字典键 */ public static void removeDictCache(String key) @@ -175,7 +229,7 @@ public class DictUtils /** * 设置cache key - * + * * @param configKey 参数键 * @return 缓存键key */ diff --git a/os-common/src/main/java/com/os/common/utils/StringUtils.java b/os-common/src/main/java/com/os/common/utils/StringUtils.java index 6e86963..4b1e714 100644 --- a/os-common/src/main/java/com/os/common/utils/StringUtils.java +++ b/os-common/src/main/java/com/os/common/utils/StringUtils.java @@ -12,7 +12,7 @@ import com.os.common.core.text.StrFormatter; /** * 字符串工具类 - * + * * @author ruoyi */ public class StringUtils extends org.apache.commons.lang3.StringUtils @@ -28,7 +28,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 获取参数不为空值 - * + * * @param value defaultValue 要判断的value * @return value 返回值 */ @@ -39,7 +39,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个Collection是否为空, 包含List,Set,Queue - * + * * @param coll 要判断的Collection * @return true:为空 false:非空 */ @@ -50,7 +50,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个Collection是否非空,包含List,Set,Queue - * + * * @param coll 要判断的Collection * @return true:非空 false:空 */ @@ -61,7 +61,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个对象数组是否为空 - * + * * @param objects 要判断的对象数组 ** @return true:为空 false:非空 */ @@ -72,7 +72,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个对象数组是否非空 - * + * * @param objects 要判断的对象数组 * @return true:非空 false:空 */ @@ -83,7 +83,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个Map是否为空 - * + * * @param map 要判断的Map * @return true:为空 false:非空 */ @@ -94,7 +94,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个Map是否为空 - * + * * @param map 要判断的Map * @return true:非空 false:空 */ @@ -105,7 +105,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个字符串是否为空串 - * + * * @param str String * @return true:为空 false:非空 */ @@ -116,7 +116,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个字符串是否为非空串 - * + * * @param str String * @return true:非空串 false:空串 */ @@ -127,7 +127,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个对象是否为空 - * + * * @param object Object * @return true:为空 false:非空 */ @@ -138,7 +138,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个对象是否非空 - * + * * @param object Object * @return true:非空 false:空 */ @@ -149,7 +149,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * * 判断一个对象是否是数组类型(Java基本型别的数组) - * + * * @param object 对象 * @return true:是数组 false:不是数组 */ @@ -211,7 +211,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 截取字符串 - * + * * @param str 字符串 * @param start 开始 * @return 结果 @@ -242,7 +242,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 截取字符串 - * + * * @param str 字符串 * @param start 开始 * @param end 结束 @@ -286,9 +286,35 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils return str.substring(start, end); } + /** + * 在字符串中查找第一个出现的 `open` 和最后一个出现的 `close` 之间的子字符串 + * + * @param str 要截取的字符串 + * @param open 起始字符串 + * @param close 结束字符串 + * @return 截取结果 + */ + public static String substringBetweenLast(final String str, final String open, final String close) + { + if (isEmpty(str) || isEmpty(open) || isEmpty(close)) + { + return NULLSTR; + } + final int start = str.indexOf(open); + if (start != INDEX_NOT_FOUND) + { + final int end = str.lastIndexOf(close); + if (end != INDEX_NOT_FOUND) + { + return str.substring(start + open.length(), end); + } + } + return NULLSTR; + } + /** * 判断是否为空,并且不是空白字符 - * + * * @param str 要判断的value * @return 结果 */ @@ -318,7 +344,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
* 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
* 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
- * + * * @param template 文本模板,被替换的部分用 {} 表示 * @param params 参数值 * @return 格式化后的文本 @@ -334,7 +360,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 是否为http(s)://开头 - * + * * @param link 链接 * @return 结果 */ @@ -345,7 +371,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 字符串转set - * + * * @param str 字符串 * @param sep 分隔符 * @return set集合 @@ -357,7 +383,19 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 字符串转list - * + * + * @param str 字符串 + * @param sep 分隔符 + * @return list集合 + */ + public static final List str2List(String str, String sep) + { + return str2List(str, sep, true, false); + } + + /** + * 字符串转list + * * @param str 字符串 * @param sep 分隔符 * @param filterBlank 过滤纯空白 @@ -494,7 +532,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 是否包含字符串 - * + * * @param str 验证字符串 * @param strs 字符串组 * @return 包含返回true @@ -516,7 +554,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld - * + * * @param name 转换前的下划线大写方式命名的字符串 * @return 转换后的驼峰式命名的字符串 */ @@ -590,7 +628,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 - * + * * @param str 指定字符串 * @param strs 需要检查的字符串数组 * @return 是否匹配 @@ -612,11 +650,11 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils } /** - * 判断url是否与规则配置: - * ? 表示单个字符; - * * 表示一层路径内的任意字符串,不可跨层级; + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; * ** 表示任意层路径; - * + * * @param pattern 匹配规则 * @param url 需要匹配的url * @return @@ -635,7 +673,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 - * + * * @param num 数字对象 * @param size 字符串指定长度 * @return 返回数字的字符串格式,该字符串为指定长度。 @@ -647,7 +685,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 - * + * * @param s 原始字符串 * @param size 字符串指定长度 * @param c 用于补齐的字符 diff --git a/os-common/src/main/java/com/os/common/utils/file/FileUtils.java b/os-common/src/main/java/com/os/common/utils/file/FileUtils.java index a19d329..710ec74 100644 --- a/os-common/src/main/java/com/os/common/utils/file/FileUtils.java +++ b/os-common/src/main/java/com/os/common/utils/file/FileUtils.java @@ -11,6 +11,10 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + +import com.os.common.utils.file.FileTypeUtils; +import com.os.common.utils.file.FileUploadUtils; +import com.os.common.utils.file.MimeTypeUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; import com.os.common.config.RuoYiConfig; @@ -21,7 +25,7 @@ import org.apache.commons.io.FilenameUtils; /** * 文件处理工具类 - * + * * @author ruoyi */ public class FileUtils @@ -30,7 +34,7 @@ public class FileUtils /** * 输出指定文件的byte数组 - * + * * @param filePath 文件路径 * @param os 输出流 * @return @@ -105,7 +109,7 @@ public class FileUtils /** * 删除文件 - * + * * @param filePath 文件 * @return */ @@ -123,7 +127,7 @@ public class FileUtils /** * 文件名称验证 - * + * * @param filename 文件名称 * @return true 正常 false 非法 */ @@ -134,7 +138,7 @@ public class FileUtils /** * 检查文件是否可下载 - * + * * @param resource 需要下载的文件 * @return true 正常 false 非法 */ @@ -158,7 +162,7 @@ public class FileUtils /** * 下载文件名重新编码 - * + * * @param request 请求对象 * @param fileName 文件名 * @return 编码后的文件名 @@ -228,7 +232,7 @@ public class FileUtils /** * 获取图像后缀 - * + * * @param photoByte 图像数据 * @return 后缀名 */ @@ -257,7 +261,7 @@ public class FileUtils /** * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png - * + * * @param fileName 路径名称 * @return 没有文件路径的名称 */ @@ -275,7 +279,7 @@ public class FileUtils /** * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi - * + * * @param fileName 路径名称 * @return 没有文件路径和后缀的名称 */ @@ -285,6 +289,7 @@ public class FileUtils { return null; } - return FilenameUtils.getBaseName(fileName); + String baseName = FilenameUtils.getBaseName(fileName); + return baseName; } } diff --git a/os-common/src/main/java/com/os/common/utils/file/ImageExcelUtils.java b/os-common/src/main/java/com/os/common/utils/file/ImageExcelUtils.java deleted file mode 100644 index c120bfb..0000000 --- a/os-common/src/main/java/com/os/common/utils/file/ImageExcelUtils.java +++ /dev/null @@ -1,398 +0,0 @@ -package com.os.common.utils.file; - -import org.apache.poi.ss.usermodel.*; -import org.apache.poi.xssf.usermodel.*; -import org.apache.poi.xssf.streaming.SXSSFWorkbook; -import org.apache.poi.hssf.usermodel.*; -import org.apache.poi.ooxml.POIXMLDocumentPart; -import org.apache.poi.openxml4j.exceptions.InvalidFormatException; -import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.os.common.config.RuoYiConfig; -import com.os.common.utils.DateUtils; -import com.os.common.utils.StringUtils; -import com.os.common.utils.uuid.IdUtils; - -import javax.imageio.ImageIO; -import java.awt.image.BufferedImage; -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.*; - -/** - * Excel图片处理工具类 - * 专门处理Excel中图片的导入导出功能 - * - * @author ruoyi - * @date 2025-01-28 - */ -public class ImageExcelUtils { - - private static final Logger log = LoggerFactory.getLogger(ImageExcelUtils.class); - - /** - * 支持的图片格式 - */ - private static final String[] SUPPORTED_IMAGE_FORMATS = {"jpg", "jpeg", "png", "gif", "bmp"}; - - /** - * 图片存储基础路径 - */ - private static final String IMAGE_BASE_PATH = RuoYiConfig.getProfile() + "/excel-images/"; - - static { - // 确保图片存储目录存在 - try { - Files.createDirectories(Paths.get(IMAGE_BASE_PATH)); - } catch (IOException e) { - log.error("创建图片存储目录失败: {}", e.getMessage()); - } - } - - /** - * 向Excel单元格插入图片 - * - * @param workbook 工作簿 - * @param sheet 工作表 - * @param row 行号 - * @param col 列号 - * @param imagePath 图片路径 - * @param cellWidth 单元格宽度 - * @param cellHeight 单元格高度 - */ - public static void insertImageToCell(Workbook workbook, Sheet sheet, int row, int col, - String imagePath, int cellWidth, int cellHeight) { - try { - if (StringUtils.isEmpty(imagePath)) { - return; - } - - // 获取图片数据 - byte[] imageData = getImageData(imagePath); - if (imageData == null || imageData.length == 0) { - log.warn("图片数据为空: {}", imagePath); - return; - } - - // 确定图片类型 - int pictureType = getPictureType(imagePath); - if (pictureType == -1) { - log.warn("不支持的图片格式: {}", imagePath); - return; - } - - // 添加图片到工作簿 - int pictureIdx = workbook.addPicture(imageData, pictureType); - - // 创建绘图对象 - Drawing drawing = sheet.createDrawingPatriarch(); - - // 创建锚点 - ClientAnchor anchor = createAnchor(workbook, col, row, col + 1, row + 1); - - // 插入图片 - Picture picture = drawing.createPicture(anchor, pictureIdx); - - // 调整图片大小以适应单元格 - resizePictureToFitCell(picture, cellWidth, cellHeight); - - log.debug("成功插入图片到单元格[{},{}]: {}", row, col, imagePath); - - } catch (Exception e) { - log.error("插入图片到Excel失败: {}", e.getMessage(), e); - } - } - - /** - * 从Excel单元格提取图片 - * - * @param sheet 工作表 - * @param workbook 工作簿 - * @return 图片位置映射 key: "行_列", value: 保存的图片路径 - */ - public static Map extractImagesFromSheet(Sheet sheet, Workbook workbook) { - Map imageMap = new HashMap<>(); - - try { - if (workbook instanceof XSSFWorkbook) { - extractXSSFImages(sheet, (XSSFWorkbook) workbook, imageMap); - } else if (workbook instanceof HSSFWorkbook) { - extractHSSFImages(sheet, (HSSFWorkbook) workbook, imageMap); - } - } catch (Exception e) { - log.error("从Excel提取图片失败: {}", e.getMessage(), e); - } - - return imageMap; - } - - /** - * 提取XLSX格式的图片 - */ - private static void extractXSSFImages(Sheet sheet, XSSFWorkbook workbook, Map imageMap) { - try { - for (POIXMLDocumentPart part : ((XSSFSheet) sheet).getRelations()) { - if (part instanceof XSSFDrawing) { - XSSFDrawing drawing = (XSSFDrawing) part; - List shapes = drawing.getShapes(); - - for (XSSFShape shape : shapes) { - if (shape instanceof XSSFPicture) { - XSSFPicture picture = (XSSFPicture) shape; - XSSFClientAnchor anchor = picture.getPreferredSize(); - CTMarker from = anchor.getFrom(); - - int row = from.getRow(); - int col = from.getCol(); - String cellKey = row + "_" + col; - - // 保存图片 - String savedPath = saveImageFromExcel(picture.getPictureData()); - if (StringUtils.isNotEmpty(savedPath)) { - imageMap.put(cellKey, savedPath); - log.debug("提取图片从单元格[{},{}]: {}", row, col, savedPath); - } - } - } - } - } - } catch (Exception e) { - log.error("提取XLSX图片失败: {}", e.getMessage(), e); - } - } - - /** - * 提取XLS格式的图片 - */ - private static void extractHSSFImages(Sheet sheet, HSSFWorkbook workbook, Map imageMap) { - try { - List pictures = workbook.getAllPictures(); - if (pictures.isEmpty()) { - return; - } - - if (sheet.getDrawingPatriarch() != null) { - HSSFPatriarch patriarch = (HSSFPatriarch) sheet.getDrawingPatriarch(); - for (HSSFShape shape : patriarch.getChildren()) { - if (shape instanceof HSSFPicture) { - HSSFPicture picture = (HSSFPicture) shape; - HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor(); - - int row = anchor.getRow1(); - int col = anchor.getCol1(); - String cellKey = row + "_" + col; - - // 获取图片数据 - int pictureIndex = picture.getPictureIndex() - 1; - if (pictureIndex >= 0 && pictureIndex < pictures.size()) { - HSSFPictureData pictureData = pictures.get(pictureIndex); - String savedPath = saveImageFromExcel(pictureData); - if (StringUtils.isNotEmpty(savedPath)) { - imageMap.put(cellKey, savedPath); - log.debug("提取图片从单元格[{},{}]: {}", row, col, savedPath); - } - } - } - } - } - } catch (Exception e) { - log.error("提取XLS图片失败: {}", e.getMessage(), e); - } - } - - /** - * 保存从Excel提取的图片 - */ - private static String saveImageFromExcel(PictureData pictureData) { - try { - byte[] data = pictureData.getData(); - if (data == null || data.length == 0) { - return null; - } - - // 根据图片数据确定文件扩展名 - String extension = getImageExtension(data); - - // 生成唯一文件名 - String fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; - String fullPath = IMAGE_BASE_PATH + fileName; - - // 确保目录存在 - File file = new File(fullPath); - if (!file.getParentFile().exists()) { - file.getParentFile().mkdirs(); - } - - // 保存文件 - Files.write(Paths.get(fullPath), data); - - // 返回相对路径 - return "/excel-images/" + fileName; - - } catch (Exception e) { - log.error("保存Excel图片失败: {}", e.getMessage(), e); - return null; - } - } - - /** - * 获取图片数据 - */ - private static byte[] getImageData(String imagePath) { - try { - String fullPath = null; - - // 1. 处理绝对路径(包含驱动器盘符如 C:) - if (imagePath.contains(":")) { - fullPath = imagePath; - } - // 2. 处理新格式的excel-images路径 - else if (imagePath.startsWith("/excel-images/")) { - fullPath = RuoYiConfig.getProfile() + imagePath; - } - // 3. 处理传统的profile路径格式(如 /profile/upload/...) - else if (imagePath.startsWith("/profile/")) { - fullPath = RuoYiConfig.getProfile() + imagePath.substring("/profile".length()); - } - // 4. 处理以/开头的其他路径 - else if (imagePath.startsWith("/")) { - fullPath = RuoYiConfig.getProfile() + imagePath; - } - // 5. 处理相对路径 - else { - fullPath = RuoYiConfig.getProfile() + "/" + imagePath; - } - - File imageFile = new File(fullPath); - if (!imageFile.exists()) { - log.warn("图片文件不存在: {}", fullPath); - return null; - } - - return Files.readAllBytes(imageFile.toPath()); - - } catch (Exception e) { - log.error("读取图片数据失败: {}", e.getMessage(), e); - return null; - } - } - - /** - * 根据文件路径确定POI图片类型 - */ - private static int getPictureType(String imagePath) { - String extension = FileUtils.getFileExtendName(imagePath.getBytes()).toLowerCase(); - - switch (extension) { - case "jpg": - case "jpeg": - return Workbook.PICTURE_TYPE_JPEG; - case "png": - return Workbook.PICTURE_TYPE_PNG; - case "gif": - // GIF格式在POI 4.1.2中不直接支持,使用JPEG作为默认 - return Workbook.PICTURE_TYPE_JPEG; - case "bmp": - // BMP格式在POI 4.1.2中不直接支持,使用JPEG作为默认 - return Workbook.PICTURE_TYPE_JPEG; - default: - return -1; - } - } - - /** - * 根据图片数据确定文件扩展名 - */ - private static String getImageExtension(byte[] imageData) { - if (imageData.length >= 4) { - // JPEG - if (imageData[0] == (byte) 0xFF && imageData[1] == (byte) 0xD8) { - return "jpg"; - } - // PNG - if (imageData[0] == (byte) 0x89 && imageData[1] == (byte) 0x50 && - imageData[2] == (byte) 0x4E && imageData[3] == (byte) 0x47) { - return "png"; - } - // GIF - if (imageData[0] == (byte) 0x47 && imageData[1] == (byte) 0x49 && imageData[2] == (byte) 0x46) { - return "gif"; - } - // BMP - if (imageData[0] == (byte) 0x42 && imageData[1] == (byte) 0x4D) { - return "bmp"; - } - } - return "jpg"; // 默认为jpg - } - - /** - * 创建锚点 - */ - private static ClientAnchor createAnchor(Workbook workbook, int col1, int row1, int col2, int row2) { - if (workbook instanceof XSSFWorkbook || workbook instanceof SXSSFWorkbook) { - return new XSSFClientAnchor(0, 0, 0, 0, col1, row1, col2, row2); - } else { - return new HSSFClientAnchor(0, 0, 0, 0, (short) col1, row1, (short) col2, row2); - } - } - - /** - * 调整图片大小以适应单元格 - */ - private static void resizePictureToFitCell(Picture picture, int cellWidth, int cellHeight) { - try { - // 如果没有指定尺寸,使用默认调整 - if (cellWidth <= 0 || cellHeight <= 0) { - picture.resize(); - } else { - // 根据指定尺寸调整 - picture.resize(cellWidth / 256.0, cellHeight / 20.0); - } - } catch (Exception e) { - log.warn("调整图片尺寸失败: {}", e.getMessage()); - // 使用默认调整作为后备方案 - try { - picture.resize(); - } catch (Exception ex) { - log.error("默认图片调整也失败: {}", ex.getMessage()); - } - } - } - - /** - * 验证图片格式是否支持 - */ - public static boolean isSupportedImageFormat(String fileName) { - if (StringUtils.isEmpty(fileName)) { - return false; - } - - String extension = FileUtils.getFileExtendName(fileName.getBytes()).toLowerCase(); - return Arrays.asList(SUPPORTED_IMAGE_FORMATS).contains(extension); - } - - /** - * 清理临时图片文件 - * - * @param imagePaths 图片路径列表 - */ - public static void cleanupTempImages(List imagePaths) { - if (imagePaths == null || imagePaths.isEmpty()) { - return; - } - - for (String imagePath : imagePaths) { - try { - if (StringUtils.isNotEmpty(imagePath) && imagePath.contains("/excel-images/")) { - String fullPath = RuoYiConfig.getProfile() + imagePath; - Files.deleteIfExists(Paths.get(fullPath)); - } - } catch (Exception e) { - log.warn("删除临时图片失败: {}", e.getMessage()); - } - } - } -} \ No newline at end of file diff --git a/os-common/src/main/java/com/os/common/utils/poi/ExcelUtil.java b/os-common/src/main/java/com/os/common/utils/poi/ExcelUtil.java index 2dca172..4c920f6 100644 --- a/os-common/src/main/java/com/os/common/utils/poi/ExcelUtil.java +++ b/os-common/src/main/java/com/os/common/utils/poi/ExcelUtil.java @@ -24,6 +24,8 @@ import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; import javax.servlet.http.HttpServletResponse; + +import com.os.common.utils.poi.ExcelHandlerAdapter; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.RegExUtils; import org.apache.commons.lang3.reflect.FieldUtils; @@ -88,13 +90,15 @@ import com.os.common.utils.reflect.ReflectUtils; /** * Excel相关处理 - * + * * @author ruoyi */ public class ExcelUtil { private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + public static final String SEPARATOR = ","; + public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; public static final String[] FORMULA_STR = { "=", "-", "+", "@" }; @@ -194,6 +198,11 @@ public class ExcelUtil */ public Class clazz; + /** + * 需要显示列属性 + */ + public String[] includeFields; + /** * 需要排除列属性 */ @@ -204,11 +213,20 @@ public class ExcelUtil this.clazz = clazz; } + /** + * 仅在Excel中显示列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + */ + public void showColumn(String... fields) + { + this.includeFields = fields; + } + /** * 隐藏Excel中列属性 * * @param fields 列属性名 示例[单个"name"/多个"id","name"] - * @throws Exception */ public void hideColumn(String... fields) { @@ -238,8 +256,6 @@ public class ExcelUtil { if (StringUtils.isNotEmpty(title)) { - subMergedFirstRowNum++; - subMergedLastRowNum++; int titleLastCol = this.fields.size() - 1; if (isSubList()) { @@ -250,7 +266,7 @@ public class ExcelUtil Cell titleCell = titleRow.createCell(0); titleCell.setCellStyle(styles.get("title")); titleCell.setCellValue(title); - sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol)); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), 0, titleLastCol)); } } @@ -261,23 +277,31 @@ public class ExcelUtil { if (isSubList()) { - subMergedFirstRowNum++; - subMergedLastRowNum++; Row subRow = sheet.createRow(rownum); - int excelNum = 0; + int column = 0; + int subFieldSize = subFields != null ? subFields.size() : 0; for (Object[] objects : fields) { + Field field = (Field) objects[0]; Excel attr = (Excel) objects[1]; - Cell headCell1 = subRow.createCell(excelNum); - headCell1.setCellValue(attr.name()); - headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); - excelNum++; - } - int headFirstRow = excelNum - 1; - int headLastRow = headFirstRow + subFields.size() - 1; - if (headLastRow > headFirstRow) - { - sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow)); + if (Collection.class.isAssignableFrom(field.getType())) + { + Cell cell = subRow.createCell(column); + cell.setCellValue(attr.name()); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + if (subFieldSize > 1) + { + CellRangeAddress cellAddress = new CellRangeAddress(rownum, rownum, column, column + subFieldSize - 1); + sheet.addMergedRegion(cellAddress); + } + column += subFieldSize; + } + else + { + Cell cell = subRow.createCell(column++); + cell.setCellValue(attr.name()); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + } } rownum++; } @@ -285,16 +309,28 @@ public class ExcelUtil /** * 对excel表单默认第一个索引名转换成list - * + * * @param is 输入流 * @return 转换后集合 */ public List importExcel(InputStream is) + { + return importExcel(is, 0); + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @param titleNum 标题占用行数 + * @return 转换后集合 + */ + public List importExcel(InputStream is, int titleNum) { List list = null; try { - list = importExcel(is, 0); + list = importExcel(StringUtils.EMPTY, is, titleNum); } catch (Exception e) { @@ -308,21 +344,9 @@ public class ExcelUtil return list; } - /** - * 对excel表单默认第一个索引名转换成list - * - * @param is 输入流 - * @param titleNum 标题占用行数 - * @return 转换后集合 - */ - public List importExcel(InputStream is, int titleNum) throws Exception - { - return importExcel(StringUtils.EMPTY, is, titleNum); - } - /** * 对excel表单指定表格索引名转换成list - * + * * @param sheetName 表格索引名 * @param titleNum 标题占用行数 * @param is 输入流 @@ -340,7 +364,7 @@ public class ExcelUtil throw new IOException("文件sheet不存在"); } boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook); - Map pictures; + Map> pictures = null; if (isXSSFWorkbook) { pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb); @@ -406,7 +430,7 @@ public class ExcelUtil if (String.class == fieldType) { String s = Convert.toStr(val); - if (StringUtils.endsWith(s, ".0")) + if (s.matches("^\\d+\\.0$")) { val = StringUtils.substringBefore(s, ".0"); } @@ -471,7 +495,12 @@ public class ExcelUtil } else if (StringUtils.isNotEmpty(attr.dictType())) { - val = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); + if (!sysDictMap.containsKey(attr.dictType() + val)) + { + String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); + sysDictMap.put(attr.dictType() + val, dictValue); + } + val = sysDictMap.get(attr.dictType() + val); } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) { @@ -479,16 +508,15 @@ public class ExcelUtil } else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures)) { - PictureData image = pictures.get(row.getRowNum() + "_" + entry.getKey()); - if (image == null) + StringBuilder propertyString = new StringBuilder(); + List images = pictures.get(row.getRowNum() + "_" + entry.getKey()); + for (PictureData picture : images) { - val = ""; - } - else - { - byte[] data = image.getData(); - val = FileUtils.writeImportBytes(data); + byte[] data = picture.getData(); + String fileName = FileUtils.writeImportBytes(data); + propertyString.append(fileName).append(SEPARATOR); } + val = StringUtils.stripEnd(propertyString.toString(), SEPARATOR); } ReflectUtils.invokeSetter(entity, propertyName, val); } @@ -501,7 +529,7 @@ public class ExcelUtil /** * 对list数据源将其里面的数据导入到excel表单 - * + * * @param list 导出数据集合 * @param sheetName 工作表的名称 * @return 结果 @@ -513,7 +541,7 @@ public class ExcelUtil /** * 对list数据源将其里面的数据导入到excel表单 - * + * * @param list 导出数据集合 * @param sheetName 工作表的名称 * @param title 标题 @@ -527,7 +555,7 @@ public class ExcelUtil /** * 对list数据源将其里面的数据导入到excel表单 - * + * * @param response 返回数据 * @param list 导出数据集合 * @param sheetName 工作表的名称 @@ -540,7 +568,7 @@ public class ExcelUtil /** * 对list数据源将其里面的数据导入到excel表单 - * + * * @param response 返回数据 * @param list 导出数据集合 * @param sheetName 工作表的名称 @@ -557,7 +585,7 @@ public class ExcelUtil /** * 对list数据源将其里面的数据导入到excel表单 - * + * * @param sheetName 工作表的名称 * @return 结果 */ @@ -568,7 +596,7 @@ public class ExcelUtil /** * 对list数据源将其里面的数据导入到excel表单 - * + * * @param sheetName 工作表的名称 * @param title 标题 * @return 结果 @@ -581,7 +609,7 @@ public class ExcelUtil /** * 对list数据源将其里面的数据导入到excel表单 - * + * * @param sheetName 工作表的名称 * @return 结果 */ @@ -592,7 +620,7 @@ public class ExcelUtil /** * 对list数据源将其里面的数据导入到excel表单 - * + * * @param sheetName 工作表的名称 * @param title 标题 * @return 结果 @@ -607,7 +635,7 @@ public class ExcelUtil /** * 对list数据源将其里面的数据导入到excel表单 - * + * * @return 结果 */ public void exportExcel(HttpServletResponse response) @@ -629,7 +657,7 @@ public class ExcelUtil /** * 对list数据源将其里面的数据导入到excel表单 - * + * * @return 结果 */ public AjaxResult exportExcel() @@ -697,7 +725,7 @@ public class ExcelUtil /** * 填充excel数据 - * + * * @param index 序号 * @param row 单元格行 */ @@ -706,69 +734,96 @@ public class ExcelUtil { int startNo = index * sheetSize; int endNo = Math.min(startNo + sheetSize, list.size()); - int rowNo = (1 + rownum) - startNo; + int currentRowNum = rownum + 1; // 从标题行后开始 + for (int i = startNo; i < endNo; i++) { - rowNo = isSubList() ? (i > 1 ? rowNo + 1 : rowNo + i) : i + 1 + rownum - startNo; - row = sheet.createRow(rowNo); - // 得到导出对象. + row = sheet.createRow(currentRowNum); T vo = (T) list.get(i); - Collection subList = null; - if (isSubList()) - { - if (isSubListValue(vo)) - { - subList = getListCellValue(vo); - subMergedLastRowNum = subMergedLastRowNum + subList.size(); - } - else - { - subMergedFirstRowNum++; - subMergedLastRowNum++; - } - } int column = 0; + int maxSubListSize = getCurrentMaxSubListSize(vo); for (Object[] os : fields) { Field field = (Field) os[0]; Excel excel = (Excel) os[1]; - if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList)) + if (Collection.class.isAssignableFrom(field.getType())) { - boolean subFirst = false; - for (Object obj : subList) + try { - if (subFirst) + Collection subList = (Collection) getTargetValue(vo, field, excel); + if (subList != null && !subList.isEmpty()) { - rowNo++; - row = sheet.createRow(rowNo); - } - List subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class); - int subIndex = 0; - for (Field subField : subFields) - { - if (subField.isAnnotationPresent(Excel.class)) + int subIndex = 0; + for (Object subVo : subList) { - subField.setAccessible(true); - Excel attr = subField.getAnnotation(Excel.class); - this.addCell(attr, row, (T) obj, subField, column + subIndex); + Row subRow = sheet.getRow(currentRowNum + subIndex); + if (subRow == null) + { + subRow = sheet.createRow(currentRowNum + subIndex); + } + + int subColumn = column; + for (Field subField : subFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + addCell(subExcel, subRow, (T) subVo, subField, subColumn++); + } + subIndex++; } - subIndex++; + column += subFields.size(); } - subFirst = true; } - this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size(); + catch (Exception e) + { + log.error("填充集合数据失败", e); + } } else { - this.addCell(excel, row, vo, field, column++); + // 创建单元格并设置值 + addCell(excel, row, vo, field, column); + if (maxSubListSize > 1 && excel.needMerge()) + { + sheet.addMergedRegion(new CellRangeAddress(currentRowNum, currentRowNum + maxSubListSize - 1, column, column)); + } + column++; } } + currentRowNum += maxSubListSize; } } + /** + * 获取子列表最大数 + */ + private int getCurrentMaxSubListSize(T vo) + { + int maxSubListSize = 1; + for (Object[] os : fields) + { + Field field = (Field) os[0]; + if (Collection.class.isAssignableFrom(field.getType())) + { + try + { + Collection subList = (Collection) getTargetValue(vo, field, (Excel) os[1]); + if (subList != null && !subList.isEmpty()) + { + maxSubListSize = Math.max(maxSubListSize, subList.size()); + } + } + catch (Exception e) + { + log.error("获取集合大小失败", e); + } + } + } + return maxSubListSize; + } + /** * 创建表格样式 - * + * * @param wb 工作薄对象 * @return 样式列表 */ @@ -823,7 +878,7 @@ public class ExcelUtil /** * 根据Excel注解创建表格头样式 - * + * * @param wb 工作薄对象 * @return 自定义样式列表 */ @@ -859,7 +914,7 @@ public class ExcelUtil /** * 根据Excel注解创建表格列样式 - * + * * @param wb 工作薄对象 * @return 自定义样式列表 */ @@ -898,7 +953,7 @@ public class ExcelUtil */ public void annotationDataStyles(Map styles, Field field, Excel excel) { - String key = StringUtils.format("data_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType()); + String key = StringUtils.format("data_{}_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType(), excel.wrapText()); if (!styles.containsKey(key)) { CellStyle style = wb.createCellStyle(); @@ -914,6 +969,7 @@ public class ExcelUtil style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); style.setFillPattern(FillPatternType.SOLID_FOREGROUND); style.setFillForegroundColor(excel.backgroundColor().getIndex()); + style.setWrapText(excel.wrapText()); Font dataFont = wb.createFont(); dataFont.setFontName("Arial"); dataFont.setFontHeightInPoints((short) 10); @@ -942,7 +998,7 @@ public class ExcelUtil if (isSubList()) { // 填充默认样式,防止合并单元格样式失效 - sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType()))); + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText()))); if (attr.needMerge()) { sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); @@ -953,7 +1009,7 @@ public class ExcelUtil /** * 设置单元格信息 - * + * * @param value 单元格值 * @param attr 注解相关 * @param cell 单元格信息 @@ -984,12 +1040,15 @@ public class ExcelUtil else if (ColumnType.IMAGE == attr.cellType()) { ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); - String imagePath = Convert.toStr(value); - if (StringUtils.isNotEmpty(imagePath)) + String propertyValue = Convert.toStr(value); + if (StringUtils.isNotEmpty(propertyValue)) { - byte[] data = ImageUtils.getImage(imagePath); - getDrawingPatriarch(cell.getSheet()).createPicture(anchor, - cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + List imagePaths = StringUtils.str2List(propertyValue, SEPARATOR); + for (String imagePath : imagePaths) + { + byte[] data = ImageUtils.getImage(imagePath); + getDrawingPatriarch(cell.getSheet()).createPicture(anchor, cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } } } } @@ -1037,17 +1096,28 @@ public class ExcelUtil // 设置列宽 sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); } - if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0) + if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0 || attr.comboReadDict()) { - if (attr.combo().length > 15 || StringUtils.join(attr.combo()).length() > 255) + String[] comboArray = attr.combo(); + if (attr.comboReadDict()) + { + if (!sysDictMap.containsKey("combo_" + attr.dictType())) + { + String labels = DictUtils.getDictLabels(attr.dictType()); + sysDictMap.put("combo_" + attr.dictType(), labels); + } + String val = sysDictMap.get("combo_" + attr.dictType()); + comboArray = StringUtils.split(val, DictUtils.SEPARATOR); + } + if (comboArray.length > 15 || StringUtils.join(comboArray).length() > 255) { // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到 - setXSSFValidationWithHidden(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + setXSSFValidationWithHidden(sheet, comboArray, attr.prompt(), 1, 100, column, column); } else { // 提示信息或只能选择不能输入的列内容. - setPromptOrValidation(sheet, attr.combo(), attr.prompt(), 1, 100, column, column); + setPromptOrValidation(sheet, comboArray, attr.prompt(), 1, 100, column, column); } } } @@ -1069,10 +1139,12 @@ public class ExcelUtil cell = row.createCell(column); if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()) { - CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column); - sheet.addMergedRegion(cellAddress); + if (subMergedLastRowNum >= subMergedFirstRowNum) + { + sheet.addMergedRegion(new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column)); + } } - cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType()))); + cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText()))); // 用于读取对象中的属性 Object value = getTargetValue(vo, field, attr); @@ -1082,6 +1154,7 @@ public class ExcelUtil String dictType = attr.dictType(); if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) { + cell.getCellStyle().setDataFormat(this.wb.getCreationHelper().createDataFormat().getFormat(dateFormat)); cell.setCellValue(parseDateToStr(dateFormat, value)); } else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) @@ -1122,7 +1195,7 @@ public class ExcelUtil /** * 设置 POI XSSFSheet 单元格提示或选择框 - * + * * @param sheet 表单 * @param textlist 下拉框显示的内容 * @param promptContent 提示内容 @@ -1132,7 +1205,7 @@ public class ExcelUtil * @param endCol 结束列 */ public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, - int firstCol, int endCol) + int firstCol, int endCol) { DataValidationHelper helper = sheet.getDataValidationHelper(); DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); @@ -1211,7 +1284,7 @@ public class ExcelUtil /** * 解析导出值 0=男,1=女,2=未知 - * + * * @param propertyValue 参数值 * @param converterExp 翻译注解 * @param separator 分隔符 @@ -1220,7 +1293,7 @@ public class ExcelUtil public static String convertByExp(String propertyValue, String converterExp, String separator) { StringBuilder propertyString = new StringBuilder(); - String[] convertSource = converterExp.split(","); + String[] convertSource = converterExp.split(SEPARATOR); for (String item : convertSource) { String[] itemArray = item.split("="); @@ -1248,7 +1321,7 @@ public class ExcelUtil /** * 反向解析值 男=0,女=1,未知=2 - * + * * @param propertyValue 参数值 * @param converterExp 翻译注解 * @param separator 分隔符 @@ -1257,7 +1330,7 @@ public class ExcelUtil public static String reverseByExp(String propertyValue, String converterExp, String separator) { StringBuilder propertyString = new StringBuilder(); - String[] convertSource = converterExp.split(","); + String[] convertSource = converterExp.split(SEPARATOR); for (String item : convertSource) { String[] itemArray = item.split("="); @@ -1285,7 +1358,7 @@ public class ExcelUtil /** * 解析字典值 - * + * * @param dictValue 字典值 * @param dictType 字典类型 * @param separator 分隔符 @@ -1298,7 +1371,7 @@ public class ExcelUtil /** * 反向解析值字典值 - * + * * @param dictLabel 字典标签 * @param dictType 字典类型 * @param separator 分隔符 @@ -1311,7 +1384,7 @@ public class ExcelUtil /** * 数据处理器 - * + * * @param value 数据值 * @param excel 数据注解 * @return @@ -1382,13 +1455,12 @@ public class ExcelUtil */ public String encodingFilename(String filename) { - filename = UUID.randomUUID() + "_" + filename + ".xlsx"; - return filename; + return UUID.randomUUID() + "_" + filename + ".xlsx"; } /** * 获取下载路径 - * + * * @param filename 文件名称 */ public String getAbsoluteFile(String filename) @@ -1404,7 +1476,7 @@ public class ExcelUtil /** * 获取bean中的属性值 - * + * * @param vo 实体对象 * @param field 字段 * @param excel 注解 @@ -1413,6 +1485,7 @@ public class ExcelUtil */ private Object getTargetValue(T vo, Field field, Excel excel) throws Exception { + field.setAccessible(true); Object o = field.get(vo); if (StringUtils.isNotEmpty(excel.targetAttr())) { @@ -1435,7 +1508,7 @@ public class ExcelUtil /** * 以类的属性的get方法方法形式获取值 - * + * * @param o * @param name * @return value @@ -1472,46 +1545,83 @@ public class ExcelUtil List tempFields = new ArrayList<>(); tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); - for (Field field : tempFields) + if (StringUtils.isNotEmpty(includeFields)) { - if (!ArrayUtils.contains(this.excludeFields, field.getName())) + for (Field field : tempFields) { - // 单注解 - if (field.isAnnotationPresent(Excel.class)) + if (ArrayUtils.contains(this.includeFields, field.getName()) || field.isAnnotationPresent(Excels.class)) { - Excel attr = field.getAnnotation(Excel.class); - if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) + addField(fields, field); + } + } + } + else if (StringUtils.isNotEmpty(excludeFields)) + { + for (Field field : tempFields) + { + if (!ArrayUtils.contains(this.excludeFields, field.getName())) + { + addField(fields, field); + } + } + } + else + { + for (Field field : tempFields) + { + addField(fields, field); + } + } + return fields; + } + + /** + * 添加字段信息 + */ + public void addField(List fields, Field field) + { + // 单注解 + if (field.isAnnotationPresent(Excel.class)) + { + Excel attr = field.getAnnotation(Excel.class); + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) + { + fields.add(new Object[] { field, attr }); + } + if (Collection.class.isAssignableFrom(field.getType())) + { + subMethod = getSubMethod(field.getName(), clazz); + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + } + } + + // 多注解 + if (field.isAnnotationPresent(Excels.class)) + { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel attr : excels) + { + if (StringUtils.isNotEmpty(includeFields)) + { + if (ArrayUtils.contains(this.includeFields, field.getName() + "." + attr.targetAttr()) + && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) { - field.setAccessible(true); fields.add(new Object[] { field, attr }); } - if (Collection.class.isAssignableFrom(field.getType())) - { - subMethod = getSubMethod(field.getName(), clazz); - ParameterizedType pt = (ParameterizedType) field.getGenericType(); - Class subClass = (Class) pt.getActualTypeArguments()[0]; - this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); - } } - - // 多注解 - if (field.isAnnotationPresent(Excels.class)) + else { - Excels attrs = field.getAnnotation(Excels.class); - Excel[] excels = attrs.value(); - for (Excel attr : excels) + if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr()) + && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) { - if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr()) - && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) - { - field.setAccessible(true); - fields.add(new Object[] { field, attr }); - } + fields.add(new Object[] { field, attr }); } } } } - return fields; } /** @@ -1541,7 +1651,7 @@ public class ExcelUtil /** * 创建工作表 - * + * * @param sheetNo sheet数量 * @param index 序号 */ @@ -1558,7 +1668,7 @@ public class ExcelUtil /** * 获取单元格值 - * + * * @param row 获取的行 * @param column 获取单元格列号 * @return 单元格值 @@ -1618,7 +1728,7 @@ public class ExcelUtil /** * 判断是否是空行 - * + * * @param row 判断的行 * @return */ @@ -1646,30 +1756,24 @@ public class ExcelUtil * @param workbook 工作簿对象 * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData */ - public static Map getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook) + public static Map> getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook) { - Map sheetIndexPicMap = new HashMap(); + Map> sheetIndexPicMap = new HashMap<>(); List pictures = workbook.getAllPictures(); - if (!pictures.isEmpty()) + if (!pictures.isEmpty() && sheet.getDrawingPatriarch() != null) { for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) { - HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor(); if (shape instanceof HSSFPicture) { HSSFPicture pic = (HSSFPicture) shape; - int pictureIndex = pic.getPictureIndex() - 1; - HSSFPictureData picData = pictures.get(pictureIndex); + HSSFClientAnchor anchor = (HSSFClientAnchor) pic.getAnchor(); String picIndex = anchor.getRow1() + "_" + anchor.getCol1(); - sheetIndexPicMap.put(picIndex, picData); + sheetIndexPicMap.computeIfAbsent(picIndex, k -> new ArrayList<>()).add(pic.getPictureData()); } } - return sheetIndexPicMap; - } - else - { - return sheetIndexPicMap; } + return sheetIndexPicMap; } /** @@ -1679,16 +1783,15 @@ public class ExcelUtil * @param workbook 工作簿对象 * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData */ - public static Map getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook) + public static Map> getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook) { - Map sheetIndexPicMap = new HashMap(); + Map> sheetIndexPicMap = new HashMap<>(); for (POIXMLDocumentPart dr : sheet.getRelations()) { if (dr instanceof XSSFDrawing) { XSSFDrawing drawing = (XSSFDrawing) dr; - List shapes = drawing.getShapes(); - for (XSSFShape shape : shapes) + for (XSSFShape shape : drawing.getShapes()) { if (shape instanceof XSSFPicture) { @@ -1696,7 +1799,7 @@ public class ExcelUtil XSSFClientAnchor anchor = pic.getPreferredSize(); CTMarker ctMarker = anchor.getFrom(); String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol(); - sheetIndexPicMap.put(picIndex, pic.getPictureData()); + sheetIndexPicMap.computeIfAbsent(picIndex, k -> new ArrayList<>()).add(pic.getPictureData()); } } } @@ -1706,7 +1809,7 @@ public class ExcelUtil /** * 格式化不同类型的日期对象 - * + * * @param dateFormat 日期格式 * @param val 被格式化的日期对象 * @return 格式化后的日期字符 @@ -1772,7 +1875,7 @@ public class ExcelUtil /** * 获取对象的子列表方法 - * + * * @param name 名称 * @param pojoClass 类对象 * @return 子列表方法 diff --git a/os-ems/src/main/java/com/os/ems/info/domain/LuggageSystemPlcBufferBatteryLifecycle.java b/os-ems/src/main/java/com/os/ems/info/domain/LuggageSystemPlcBufferBatteryLifecycle.java index 56a05a3..3f92584 100644 --- a/os-ems/src/main/java/com/os/ems/info/domain/LuggageSystemPlcBufferBatteryLifecycle.java +++ b/os-ems/src/main/java/com/os/ems/info/domain/LuggageSystemPlcBufferBatteryLifecycle.java @@ -39,7 +39,7 @@ public class LuggageSystemPlcBufferBatteryLifecycle extends BaseEntity private String powerSource; /** 图片地址 */ - @Excel(name = "图片地址") + @Excel(name = "图片地址", cellType = Excel.ColumnType.IMAGE, width = 100, height = 50) private String imageAddress; /** 备注 */ diff --git a/os-ems/src/main/java/com/os/ems/info/domain/LuggageSystemSecurityDoorBatteryLifecycle.java b/os-ems/src/main/java/com/os/ems/info/domain/LuggageSystemSecurityDoorBatteryLifecycle.java index 734fdc9..a50989d 100644 --- a/os-ems/src/main/java/com/os/ems/info/domain/LuggageSystemSecurityDoorBatteryLifecycle.java +++ b/os-ems/src/main/java/com/os/ems/info/domain/LuggageSystemSecurityDoorBatteryLifecycle.java @@ -39,7 +39,7 @@ public class LuggageSystemSecurityDoorBatteryLifecycle extends BaseEntity private String batteryType; /** 图片地址 */ - @Excel(name = "图片地址") + @Excel(name = "图片地址", cellType = Excel.ColumnType.IMAGE, width = 100, height = 50) private String imageAddress; /** 备注 */