From 1f11cc69119883f877d898bd34691a72e3f31920 Mon Sep 17 00:00:00 2001 From: lip Date: Thu, 29 Sep 2022 10:30:40 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E8=B0=83=E6=95=B4=E3=80=81Ex?= =?UTF-8?q?cel=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/engine/model/common/FlowModelService.java | 2 + .../flow/listcard/lclc1/FlowLcLc1Service.java | 1 + .../model/flow/listcard/lcms/FlowLcMsService.java | 1 + .../default/incModel/inc_card_fields_only.ftl | 16 +- .../static/template/default/model_card_flow.ftl | 16 +- .../static/template/default/model_card_ms.ftl | 16 +- .../static/template/default/model_card_normal.ftl | 16 +- .../static/template/default/model_list_card.ftl | 16 +- smtweb-framework/core/pom.xml | 10 + .../core/mvc/service/AbstractListHandler.java | 8 + .../framework/core/util/ExcelExportUtil.java | 1081 ++++++++++++++++++++ .../cc/smtweb/framework/core/util/ExcelUtil.java | 131 +++ .../cc/smtweb/framework/core/util/ExportUtil.java | 54 + .../cc/smtweb/framework/core/util/FileUtil.java | 19 + .../cc/smtweb/framework/core/util/MathUtil.java | 507 +++++++++ .../cc/smtweb/framework/core/util/StringUtil.java | 49 + 16 files changed, 1938 insertions(+), 5 deletions(-) create mode 100644 smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExcelExportUtil.java create mode 100644 smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExcelUtil.java create mode 100644 smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExportUtil.java create mode 100644 smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/MathUtil.java diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/common/FlowModelService.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/common/FlowModelService.java index b9760c8..abc4c3f 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/common/FlowModelService.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/common/FlowModelService.java @@ -28,6 +28,8 @@ public class FlowModelService extends LCSingleService { return new FlowModelSaveHandler<>(); case TYPE_MODEL_DEL: return new FlowModelDelHandler(); + case TYPE_FLOW: + return new FlowTransHandler(); } return super.createHandler(type); } diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lclc1/FlowLcLc1Service.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lclc1/FlowLcLc1Service.java index b8c3c16..10e5982 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lclc1/FlowLcLc1Service.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lclc1/FlowLcLc1Service.java @@ -25,6 +25,7 @@ public class FlowLcLc1Service extends FlowModelService { case TYPE_MODEL_LOAD: case TYPE_MODEL_SAVE: case TYPE_MODEL_DEL: + case TYPE_FLOW: return super.createHandler(type); } return worker.createHandler(type); diff --git a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lcms/FlowLcMsService.java b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lcms/FlowLcMsService.java index 5b495f5..3c7f460 100644 --- a/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lcms/FlowLcMsService.java +++ b/smtweb-framework/bpm/src/main/java/cc/smtweb/system/bpm/web/engine/model/flow/listcard/lcms/FlowLcMsService.java @@ -22,6 +22,7 @@ public class FlowLcMsService extends FlowModelService { switch (type) { case TYPE_MODEL_LIST: case TYPE_MODEL_LOAD: + case TYPE_FLOW: return super.createHandler(type); case TYPE_MODEL_SAVE: return new FlowLcMsSaveHandler(); diff --git a/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_card_fields_only.ftl b/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_card_fields_only.ftl index 9529067..546828d 100644 --- a/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_card_fields_only.ftl +++ b/smtweb-framework/bpm/src/main/resources/static/template/default/incModel/inc_card_fields_only.ftl @@ -34,7 +34,21 @@ <#if fields.lookup??> "lookup": { <#list fields.lookup as k, v> - "${k}": "${v}" <#if k_has_next>, + <#if v?is_enumerable> + "${k}": [ + <#list v as v1> + { + <#list v1 as vk1, vv1> + "${vk1}": "${vv1}"<#if vk1_has_next>, + + } + <#if v1_has_next>, + + ] + <#else> + "${k}": "${v}" + + <#if k_has_next>, }, diff --git a/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_flow.ftl b/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_flow.ftl index b0097a1..2c03eb0 100644 --- a/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_flow.ftl +++ b/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_flow.ftl @@ -184,7 +184,21 @@ <#if fields.lookup??> "lookup": { <#list fields.lookup as k, v> - "${k}": "${v}" <#if k_has_next>, + <#if v?is_enumerable> + "${k}": [ + <#list v as v1> + { + <#list v1 as vk1, vv1> + "${vk1}": "${vv1}"<#if vk1_has_next>, + + } + <#if v1_has_next>, + + ] + <#else> + "${k}": "${v}" + + <#if k_has_next>, }, diff --git a/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_ms.ftl b/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_ms.ftl index b4de32d..e3b246d 100644 --- a/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_ms.ftl +++ b/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_ms.ftl @@ -80,7 +80,21 @@ <#if field.lookup??> "lookup": { <#list field.lookup as k, v> - "${k}": "${v}" <#if k_has_next>, + <#if v?is_enumerable> + "${k}": [ + <#list v as v1> + { + <#list v1 as vk1, vv1> + "${vk1}": "${vv1}"<#if vk1_has_next>, + + } + <#if v1_has_next>, + + ] + <#else> + "${k}": "${v}" + + <#if k_has_next>, }, diff --git a/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_normal.ftl b/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_normal.ftl index e4cc8b2..c322324 100644 --- a/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_normal.ftl +++ b/smtweb-framework/bpm/src/main/resources/static/template/default/model_card_normal.ftl @@ -165,7 +165,21 @@ <#if field.lookup??> "lookup": { <#list field.lookup as k, v> - "${k}": "${v}" <#if k_has_next>, + <#if v?is_enumerable> + "${k}": [ + <#list v as v1> + { + <#list v1 as vk1, vv1> + "${vk1}": "${vv1}"<#if vk1_has_next>, + + } + <#if v1_has_next>, + + ] + <#else> + "${k}": "${v}" + + <#if k_has_next>, }, diff --git a/smtweb-framework/bpm/src/main/resources/static/template/default/model_list_card.ftl b/smtweb-framework/bpm/src/main/resources/static/template/default/model_list_card.ftl index 6547a5d..682c99c 100644 --- a/smtweb-framework/bpm/src/main/resources/static/template/default/model_list_card.ftl +++ b/smtweb-framework/bpm/src/main/resources/static/template/default/model_list_card.ftl @@ -168,7 +168,21 @@ <#if dfield.lookup??> "lookup": { <#list dfield.lookup as k, v> - "${k}": "${v}" <#if k_has_next>, + <#if v?is_enumerable> + "${k}": [ + <#list v as v1> + { + <#list v1 as vk1, vv1> + "${vk1}": "${vv1}"<#if vk1_has_next>, + + } + <#if v1_has_next>, + + ] + <#else> + "${k}": "${v}" + + <#if k_has_next>, }, diff --git a/smtweb-framework/core/pom.xml b/smtweb-framework/core/pom.xml index 4e524ba..1ff619f 100644 --- a/smtweb-framework/core/pom.xml +++ b/smtweb-framework/core/pom.xml @@ -168,6 +168,16 @@ spring-boot-starter-test test + + org.apache.poi + poi + 5.2.2 + + + org.apache.poi + poi-ooxml + 5.2.2 + diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/AbstractListHandler.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/AbstractListHandler.java index c64bf1b..3813a8f 100644 --- a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/AbstractListHandler.java +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/service/AbstractListHandler.java @@ -54,6 +54,14 @@ public abstract class AbstractListHandler extends AbstractHandler { return R.success(buildListData()); } + /** + * 导出excel + * @return R:{ file: fileName@@filePath } + */ + public R exportExcel() { + return R.success(buildListData()); + } + public SwListData buildListData() { List listData; SqlPara sqlPara = buildDataSql(); diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExcelExportUtil.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExcelExportUtil.java new file mode 100644 index 0000000..c6b3abd --- /dev/null +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExcelExportUtil.java @@ -0,0 +1,1081 @@ +package cc.smtweb.framework.core.util; + +import cc.smtweb.framework.core.common.SwMap; +import org.apache.commons.io.IOUtils; +import org.apache.poi.hssf.usermodel.HSSFClientAnchor; +import org.apache.poi.hssf.usermodel.HSSFDataFormat; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellUtil; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import sun.misc.BASE64Decoder; + +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导出工具类 + */ +public class ExcelExportUtil { + public static Integer MAX_EXCEL_COUNT = 60000; + private static Map> cacheMaps = null; + + public static void clearCellStyleCache(Workbook web) { + if (cacheMaps != null) { + cacheMaps.remove(web); + } + } + + public static CellStyle createCellStyle(Workbook wb, short fontSize, boolean isBold, boolean hasBorder, HorizontalAlignment align) { + CellStyle cs = getCellStyle(wb, fontSize + "-" + isBold + "-" + hasBorder + "-" + align); + Font f = wb.createFont(); + f.setFontHeightInPoints(fontSize); + f.setBold(isBold); + cs.setFont(f); + if (hasBorder) { + cs.setBorderBottom(BorderStyle.THIN); + cs.setBorderLeft(BorderStyle.THIN); + cs.setBorderRight(BorderStyle.THIN); + cs.setBorderTop(BorderStyle.THIN); + } + cs.setAlignment(align); + cs.setVerticalAlignment(VerticalAlignment.CENTER); + cs.setWrapText(true); + return cs; + } + + private static CellStyle getCellStyle(Workbook wb, String key) { + if (cacheMaps == null) { + cacheMaps = new HashMap<>(); + } + if (cacheMaps.size() > 1000) { + cacheMaps = new HashMap<>(); + } + Map map = cacheMaps.computeIfAbsent(wb, k -> new HashMap<>()); + CellStyle cellStyle = map.get(key); + if (cellStyle == null) { + cellStyle = wb.createCellStyle(); + map.put(key, cellStyle); + } + return cellStyle; + } + + public static Row createRow(Sheet sheet, int rowNum, int rowHeight) { + Row row = sheet.getRow(rowNum); + if (row != null) return row; + row = sheet.createRow(rowNum); + if (rowHeight > 0) row.setHeightInPoints(rowHeight); + return row; + } + + public static Cell createCell(Row row, int column) { + Cell cell = row.getCell(column); + if (cell != null) return cell; + return row.createCell(column); + } + + public static void writeMergeCell(Sheet sheet, int rowHeight, int fromRow, int endRow, int fromCol, int entCol, CellStyle cs, String text) { + Row row = createRow(sheet, fromRow, rowHeight); + Cell cell = createCell(row, fromCol); + cell.setCellStyle(cs); + cell.setCellValue(text); + if (endRow != fromRow || entCol != fromCol) { + sheet.addMergedRegion(new CellRangeAddress(fromRow, endRow, fromCol, entCol)); + for (int i = fromRow; i <= endRow; i++) { + row = CellUtil.getRow(i, sheet); + for (int j = fromCol; j <= entCol; j++) { + cell = CellUtil.getCell(row, (short) j); + cell.setCellStyle(cs); + } + } + } + } + + public static void writeMergeCell(Sheet sheet, int fromRow, int endRow, int fromCol, int entCol, CellStyle cs, String text) { + writeMergeCell(sheet, 0, fromRow, endRow, fromCol, entCol, cs, text); + } + + /** + * @param row 行 + * @param colIndex 列号 + * @param cs 样式 + * @param text 内容 + * @param colType 列类型 + * @param ignoreZero 是否忽略零 + */ + public static void writeCell(Row row, int colIndex, CellStyle cs, String text, ColType colType, boolean ignoreZero) { + Cell cell = createCell(row, colIndex); + if (StringUtil.isNotEmpty(text)) text = text.replace(" ", " "); + if (ColType.AMOUNT.equals(colType)) {//金额 + if (ignoreZero && MathUtil.isEqualsZero(text)) cell.setCellValue(""); + else { + cell.setCellValue(StringUtil.getDoubleIgnoreErr(text)); + } + } else if (ColType.NUMBER.equals(colType)) {//数字 + final int v = StringUtil.getIntIgnoreErr(text); + if (ignoreZero && 0 == v) cell.setCellValue(""); + else { + cell.setCellValue(v); + } + } else {//文本 + cell.setCellValue(text); + } + cell.setCellStyle(cs); + } + + /** + * 根据前端传回的导出模型导出 + * + * @param modelJson 前端传回的导出模型 + * @throws Exception + */ + public static String exportToExcel(List> data, String modelJson) throws Exception { + return exportToExcel(data, modelJson, ""); + } + + public static String exportToExcel(List> data, String modelJson, Map mapMergeField) throws Exception { + SwMap model = new SwMap(); + model.putAll(JsonUtil.parseMap(modelJson)); + return exportToExcel(data, model, mapMergeField, null, (short) 9); + } + + public static String exportToExcel(List> data, SwMap model, short defFontSize) throws Exception { + return exportToExcel(data, model, null, defFontSize); + } + + public static String exportToExcel(List> data, String modelJson, String fileName) throws Exception { + SwMap model = new SwMap(); + model.putAll(JsonUtil.parseMap(modelJson)); + return exportToExcel(data, model, fileName, (short) 9); + } + + public static String exportToExcel(List> data, SwMap model, String fileName, short defFontSize) throws Exception { + return exportToExcel(data, model, null, fileName, defFontSize); + } + + public static String exportToExcel(List> data, SwMap model, Map mapMergeField, String fileName, short defFontSize) throws Exception { +// File tempFile = File.createTempFile("xls_", ".xls", new File(System.getProperty("java.io.tmpdir"))); + // TODO: 2022/9/27 路径作测试用 + File tempFile = File.createTempFile("xls_", ".xls", new File("D:\\jjkj\\")); + try (FileOutputStream fs = new FileOutputStream(tempFile)) { + exportToExcel(fs, data, model, mapMergeField, defFontSize); + fs.flush(); + } + if (StringUtil.isEmpty(fileName)) fileName = model.readString("title"); + fileName = fileName + "_" + DateUtil.nowDateTimeString() + ".xls"; + return fileName + "@@" + tempFile.getAbsolutePath(); + } + + public static int getStrLength(String text) { + if (StringUtil.isEmpty(text)) return 1; + if (!text.contains("\n")) return text.getBytes().length; + String[] ss = text.split("\n"); + int len = 0; + for (String s : ss) { + len = Math.max(len, s.getBytes().length); + } + return len; + } + + public static void exportToExcel(OutputStream os, List> data, SwMap model, Map mapMergeField, short defFontSize) throws Exception { + HSSFWorkbook wb = new HSSFWorkbook(); + int detSize = data.size(); + if (detSize == 0) { + Sheet sheet = wb.createSheet(); + exportToExcelEx(sheet, wb, data, model, mapMergeField, defFontSize); + } else { + int sheetCount = detSize % MAX_EXCEL_COUNT; + sheetCount = sheetCount != 0 ? detSize / MAX_EXCEL_COUNT + 1 : detSize / MAX_EXCEL_COUNT; + List listSheetName = new ArrayList<>(); + for (int i = 0; i < sheetCount; i++) { + List> listDetailPage = new ArrayList<>(); + int endIndex = (i + 1) * MAX_EXCEL_COUNT; + if (endIndex > detSize) { + endIndex = detSize; + } + for (int index = i * MAX_EXCEL_COUNT; index < endIndex; index++) { + listDetailPage.add(data.get(index)); + } + String sheetName = sheetCount + "-" + (i + 1); + listSheetName.add(sheetName); + Sheet sheet = wb.createSheet(sheetName); + exportToExcelEx(sheet, wb, listDetailPage, model, mapMergeField, defFontSize); + } + //删除非我们生成的sheet + while (listSheetName.size() != wb.getNumberOfSheets()) { + if (!listSheetName.contains(wb.getSheetName(0))) { + wb.removeSheetAt(0); + } + } + } + wb.write(os); + clearCellStyleCache(wb); + } + + private static String getMergeFieldValue(String keyField, Map curRow, Map mapMergeField) { + if (mapMergeField == null || !mapMergeField.containsKey(keyField)) { + return StringUtil.checkNull(curRow.get(keyField)); + } + return StringUtil.checkNull(curRow.get(mapMergeField.get(keyField))); + } + + private static void exportToExcelEx(Sheet sheet, HSSFWorkbook wb, List> data, SwMap model, Map mapMergeField, short defFontSize) throws Exception { + CellStyle colTitleStyle; + if (model.containsKey("colTitleBold") && model.readString("colTitleBold").equals("1")) { + colTitleStyle = createCellStyle(wb, defFontSize, true, true, HorizontalAlignment.CENTER); + } else { + colTitleStyle = createCellStyle(wb, defFontSize, false, true, HorizontalAlignment.CENTER); + } + CellStyle dataStyle_left = createCellStyle(wb, defFontSize, false, true, HorizontalAlignment.LEFT); + CellStyle dataStyle_right = createCellStyle(wb, defFontSize, false, true, HorizontalAlignment.RIGHT); + CellStyle dataStyle_center = createCellStyle(wb, defFontSize, false, true, HorizontalAlignment.CENTER); + + CellStyle dataStyle_amount = createCellStyle(wb, defFontSize, false, true, HorizontalAlignment.RIGHT); + dataStyle_amount.setDataFormat(HSSFDataFormat.getBuiltinFormat("#,##0.00"));//两位小数 + + CellStyle dataStyle_number = createCellStyle(wb, defFontSize, false, true, HorizontalAlignment.CENTER); + dataStyle_number.setDataFormat(HSSFDataFormat.getBuiltinFormat("#")); + + Map mapIndention = new HashMap<>(); + mapIndention.put(0, dataStyle_left); + + //总列数,加上序号列 + int colCount = 0; + if (model.containsKey("needSeqCol")) { + colCount = model.readListMap("columns").size(); + } else { + colCount = model.readListMap("columns").size() + 1; + } + //导出表头 + int rowIndex = 0, colIndex = 0; + //写入标题行 + String s = model.readString("top"); + if (StringUtil.isNotEmpty(s)) { + CellStyle csh = createCellStyle(wb, (short) 16, true, false, HorizontalAlignment.CENTER); + writeMergeCell(sheet, 35, rowIndex, rowIndex, colIndex, colIndex + colCount - 1, csh, s); + rowIndex++; + } + s = model.readString("title"); + if (StringUtil.isNotEmpty(s)) { + CellStyle csh = createCellStyle(wb, (short) 16, true, false, HorizontalAlignment.CENTER); + writeMergeCell(sheet, 35, rowIndex, rowIndex, colIndex, colIndex + colCount - 1, csh, s); + rowIndex++; + } + //写入表头上方说明文字,一般为查询条件等 + s = model.readString("headerStr"); + if (StringUtil.isNotEmpty(s)) { + HorizontalAlignment align; + switch (s){ + case "center":{ + align = HorizontalAlignment.CENTER; + break; + } + case "right":{ + align = HorizontalAlignment.RIGHT; + break; + } + default:align = HorizontalAlignment.LEFT; + } + CellStyle csh = createCellStyle(wb, defFontSize, false, false, align); + writeMergeCell(sheet, rowIndex, rowIndex, colIndex, colIndex + colCount - 1, csh, s); + rowIndex++; + } + final List> columns = model.readListMap("columns"); + final int collen = columns.size(); + int[] colMaxCharLen = new int[collen + 1]; + //写列标题 + int i, len; + List> header = model.readListMap("header"); + for (i = 0, len = header.size(); i < len; i++) { + Map hm = header.get(i); + SwMap h = new SwMap(); + h.putAll(hm); + // + int hRow = h.readInt("row"), endRow, hCol = h.readInt("col"), colspan = h.readInt("colspan"); + if (h.readBool("isLeaf")) endRow = model.readInt("headerRowCount") - 1 + rowIndex; + else endRow = hRow + h.readInt("rowspan") - 1 + rowIndex; + String text = h.readString("title"); + int fromCol = hCol + colIndex; + writeMergeCell(sheet, hRow + rowIndex, endRow, fromCol, fromCol + colspan - 1, colTitleStyle, text); + if (colspan == 1 && fromCol < colMaxCharLen.length) colMaxCharLen[fromCol] = getStrLength(text); + } + rowIndex += model.readInt("headerRowCount"); + + int curRowIndex; + Row row; + Map curRow; + AutoMergeCols amcs = new AutoMergeCols(); + AutoMergeColInfo mci; + boolean isIgnoreZero = model.readBool("ignoreZero"); + for (i = 0, len = data.size(); i < len; i++) { + curRowIndex = rowIndex + i; + curRow = data.get(i); + row = sheet.createRow(curRowIndex); + boolean isEnd = curRowIndex == data.size() + rowIndex - 1; + //写序号列 + String rowNum = ""; + int indention = curRow.containsKey("indention") ? StringUtil.getIntIgnoreErr(curRow.get("indention")) : 0; + if (StringUtil.isEmpty(curRow.get("__sys_footer"))) rowNum = String.valueOf(i + 1); + if (!model.containsKey("needSeqCol")) { + writeCell(row, 0, dataStyle_number, rowNum, ColType.TEXT, isIgnoreZero); + } + for (int j = 0; j < collen; j++) { + Map cm = columns.get(j); + SwMap col = new SwMap(); + col.putAll(cm); + // + String keyField = col.readString("field"); + String key = StringUtil.checkNull(curRow.get(keyField)); + String value = getMergeFieldValue(keyField, curRow, mapMergeField); + CellStyle cs; + ColType ct = ColType.getColType(col.readString("colType")); + if (ct == ColType.AMOUNT) { + cs = dataStyle_amount; + } else if (ct == ColType.NUMBER) { + cs = dataStyle_number; + } else if (ct == ColType.MAIN) { + cs = mapIndention.computeIfAbsent(indention, k -> { + CellStyle cst = createCellStyle(wb, defFontSize, false, true, HorizontalAlignment.LEFT); + cst.setIndention((short) (indention * 2)); + return cst; + }); + } else { + String align = col.readString("align"); + if ("CENTER".equalsIgnoreCase(align)) cs = dataStyle_center; + else if ("RIGHT".equalsIgnoreCase(align)) cs = dataStyle_right; + else cs = dataStyle_left; + } + if (!model.containsKey("needSeqCol")) { + writeCell(row, j + 1, cs, value, ct, col.readBool("ignoreZero", isIgnoreZero)); + colMaxCharLen[j + 1] = Math.max(colMaxCharLen[j + 1], getStrLength(value)); + if (col.readBool("autoMerge")) {//需要自动合并单元格 + mci = amcs.checkMerge(j + 1, curRowIndex, key, isEnd); + if (mci != null) { + writeMergeCell(sheet, mci.row, curRowIndex - 1, j + 1, j + 1, cs, mci.value); + } + amcs.addCol(j + 1, curRowIndex, key, value); + if (i == len - 1) { + //最后一行了,也要看下有合并行没,有的话要写上 + mci = amcs.checkMergeLast(j + 1); + if (mci != null) { + writeMergeCell(sheet, mci.row, curRowIndex, j + 1, j + 1, cs, mci.value); + } + } + } + } else { + writeCell(row, j, cs, value, ct, col.readBool("ignoreZero", isIgnoreZero)); + colMaxCharLen[j] = Math.max(colMaxCharLen[j], getStrLength(value)); + if (col.readBool("autoMerge")) {//需要自动合并单元格 + mci = amcs.checkMerge(j, curRowIndex, key, isEnd); + if (mci != null) writeMergeCell(sheet, mci.row, curRowIndex - 1, j, j, cs, mci.value); + amcs.addCol(j, curRowIndex, key, value); + } + } + + } + } + //去除最后一行 + if (model.readBool("removeLastRow")) { + sheet.removeRow(sheet.getRow(sheet.getLastRowNum())); + len = 1; + } else { + len = 0; + } + + s = model.readString("footer"); + if (StringUtil.isNotEmpty(s)) { + rowIndex += data.size(); + rowIndex -= len; + CellStyle csh = createCellStyle(wb, (short) (defFontSize + 2), true, false, HorizontalAlignment.LEFT); + writeMergeCell(sheet, 30, rowIndex, rowIndex, colIndex, colIndex + colCount - 1, csh, s); + } + if (model.containsKey("footers")) { + List footers = model.readStringList("footers"); + for (int j = 0; j < footers.size(); j++) { + if (StringUtil.isNotEmpty(s)) { + rowIndex++; + } else { + if (j == 0) { + rowIndex += data.size(); + rowIndex -= len; + } else { + rowIndex++; + } + } + CellStyle csh = createCellStyle(wb, (short) (defFontSize + 2), true, false, HorizontalAlignment.LEFT); + writeMergeCell(sheet, 30, rowIndex, rowIndex, colIndex, colIndex + colCount - 1, csh, footers.get(j)); + } + } + + //自动列宽 + for (int j = 0; j <= collen; j++) { + int l = colMaxCharLen[j] + 3; + if (l < 255) { + sheet.setColumnWidth(j, l * 256); + } else { + sheet.setColumnWidth(j, 6000); + } + } + } + + public enum ColType { + AMOUNT, + NUMBER, + TEXT, + MAIN; + + public static ColType getColType(String colType) { + switch (colType.toUpperCase()) { + case "AMOUNT": //金额 + return AMOUNT; + case "NUMBER": + return NUMBER; + case "MAIN": + return MAIN; + default: + return TEXT; + } + + } + } + + public static void exportToExcelModelFile(Map header, List> listDetail, String modelFile, String savePath, int rowCount) throws Exception { + checkTemplateFile(modelFile); + boolean isExcel2003 = true; + + // 检查文件名是否为空或者是否是Excel格式的文件 + if (!modelFile.matches("^.+\\.(?i)((xls)|(xlsx))$")) { + throw new IOException("Excel文件后缀必须是xls或xlsx!"); + } + // 对文件的合法性进行验证 + if (modelFile.matches("^.+\\.(?i)(xlsx)$")) isExcel2003 = false; + + + InputStream iso = null; + File tempFile = new File(savePath); + if (tempFile.exists()) tempFile.delete(); + tempFile.createNewFile(); + + FileOutputStream fs = new FileOutputStream(tempFile); + try { + iso = Files.newInputStream(Paths.get(modelFile)); + Workbook book = isExcel2003 ? new HSSFWorkbook(iso) : new XSSFWorkbook(iso); + if (CommUtil.isEmpty(listDetail)) return; + int detSize = listDetail.size(); + int sheetCount = detSize % rowCount; + sheetCount = sheetCount != 0 ? detSize / rowCount + 1 : detSize / rowCount; + List listSheetName = new ArrayList<>(); + for (int i = 0; i < sheetCount; i++) { + List> listDetailPage = new ArrayList<>(); + int endIndex = (i + 1) * rowCount; + if (endIndex > detSize) { + endIndex = detSize; + } + for (int index = i * rowCount; index < endIndex; index++) { + listDetailPage.add(listDetail.get(index)); + } + String sheetName = sheetCount + "-" + (i + 1); + listSheetName.add(sheetName); + fillSheet(book.cloneSheet(0), header, listDetailPage, sheetName, isExcel2003); + } + //删除非我们生成的sheet + while (listSheetName.size() != book.getNumberOfSheets()) { + if (!listSheetName.contains(book.getSheetName(0))) { + book.removeSheetAt(0); + } + } + book.write(fs); + fs.flush(); + } catch (Exception ioe) { + throw new Exception("读取文件出现错误:" + ioe.getMessage()); + } finally { + IOUtils.closeQuietly(iso); + IOUtils.closeQuietly(fs); + } + } + + public static void exportToExcelModelFile(Map header, List> listDetail, String modelFile, String savePath) throws Exception { + checkTemplateFile(modelFile); + boolean isExcel2003 = true; + + // 检查文件名是否为空或者是否是Excel格式的文件 + if (!modelFile.matches("^.+\\.(?i)((xls)|(xlsx))$")) { + throw new IOException("Excel文件后缀必须是xls或xlsx!"); + } + // 对文件的合法性进行验证 + if (modelFile.matches("^.+\\.(?i)(xlsx)$")) isExcel2003 = false; + + + InputStream iso = null; + File tempFile = new File(savePath); + if (tempFile.exists()) tempFile.delete(); + tempFile.createNewFile(); + + FileOutputStream fs = new FileOutputStream(tempFile); + try { + iso = Files.newInputStream(Paths.get(modelFile)); + Workbook book = isExcel2003 ? new HSSFWorkbook(iso) : new XSSFWorkbook(iso); + + fillSheet(book.getSheetAt(0), header, listDetail, isExcel2003); + book.write(fs); + fs.flush(); + } catch (Exception ioe) { + throw new Exception("读取文件出现错误:" + ioe.getMessage()); + } finally { + IOUtils.closeQuietly(iso); + IOUtils.closeQuietly(fs); + } + } + + public static String exportToExcelModel(Map header, List> listDetail, String modelFile, String fileName) throws Exception { + checkTemplateFile(modelFile); + boolean isExcel2003 = true; + + // 检查文件名是否为空或者是否是Excel格式的文件 + if (!modelFile.matches("^.+\\.(?i)((xls)|(xlsx))$")) { + throw new IOException("Excel文件后缀必须是xls或xlsx!"); + } + // 对文件的合法性进行验证 + if (modelFile.matches("^.+\\.(?i)(xlsx)$")) isExcel2003 = false; + + + InputStream iso = null; + File tempFile = File.createTempFile("xls_", ".xls", new File(System.getProperty("java.io.tmpdir"))); + FileOutputStream fs = new FileOutputStream(tempFile); + try { + iso = Files.newInputStream(Paths.get(modelFile)); + Workbook book = isExcel2003 ? new HSSFWorkbook(iso) : new XSSFWorkbook(iso); + int detSize = listDetail.size(); + if (detSize == 0) { + Sheet sheet = book.createSheet(); + fillSheet(sheet, header, listDetail, isExcel2003); + } else { + int sheetCount = detSize % MAX_EXCEL_COUNT; + sheetCount = sheetCount != 0 ? detSize / MAX_EXCEL_COUNT + 1 : detSize / MAX_EXCEL_COUNT; + List listSheetName = new ArrayList<>(); + List list = new ArrayList<>(); + for (int i = 0; i < sheetCount; i++) { + String sheetName = sheetCount + "-" + (i + 1); + listSheetName.add(sheetName); + Sheet sheet = null; + if (i > 0) { + sheet = book.cloneSheet(0); + book.setSheetName(i, sheetName); + } else { + sheet = book.getSheetAt(0); + } + list.add(sheet); + } + for (int i = 0; i < list.size(); i++) { + List> listDetailPage = new LinkedList<>(); + int endIndex = (i + 1) * MAX_EXCEL_COUNT; + if (endIndex > detSize) { + endIndex = detSize; + } + for (int index = i * MAX_EXCEL_COUNT; index < endIndex; index++) { + listDetailPage.add(listDetail.get(index)); + } + + fillSheet(list.get(i), header, listDetailPage, isExcel2003); + } + //删除非我们生成的sheet + while (listSheetName.size() != book.getNumberOfSheets()) { + if (!listSheetName.contains(book.getSheetName(0))) { + book.removeSheetAt(0); + } + } + book.write(fs); + } + fs.flush(); + } catch (Exception ioe) { + throw new Exception("读取文件出现错误:" + ioe.getMessage()); + } finally { + IOUtils.closeQuietly(iso); + IOUtils.closeQuietly(fs); + } + + fileName = fileName + "_" + DateUtil.nowDateTimeString() + ".xls"; + return fileName + "@@" + tempFile.getAbsolutePath(); + } + + public static String exportToExcelModel(Map header, List> listSon, Map>> mapSonDetail, String modelFile, String fileName) throws Exception { + checkTemplateFile(modelFile); + boolean isExcel2003 = true; + + // 检查文件名是否为空或者是否是Excel格式的文件 + if (!modelFile.matches("^.+\\.(?i)((xls)|(xlsx))$")) { + throw new IOException("Excel文件后缀必须是xls或xlsx!"); + } + // 对文件的合法性进行验证 + if (modelFile.matches("^.+\\.(?i)(xlsx)$")) isExcel2003 = false; + + + InputStream iso = null; +// File tempFile = File.createTempFile("xls_", ".xls", new File(System.getProperty("java.io.tmpdir"))); + File tempFile = File.createTempFile("xls_", ".xls", new File("D:\\jjkj\\")); + FileOutputStream fs = new FileOutputStream(tempFile); + try { + iso = Files.newInputStream(Paths.get(modelFile)); + Workbook book = isExcel2003 ? new HSSFWorkbook(iso) : new XSSFWorkbook(iso); + for (int i = 0, len = book.getNumberOfSheets(); i < len; i++) { + Sheet sheet = book.getSheetAt(i); + final String sheetName = sheet.getSheetName(); + if (!sheetName.contains("{@dyn@}")) fillSheet(sheet, header, listSon, isExcel2003); + else { + int cloneCount = 0; + for (Map stringStringMap : listSon) { + String s = sheetName.replace("{@dyn@}", "").replace("{#", "").replace("#}", ""); + s = stringStringMap.get(s); + if (!mapSonDetail.containsKey(s)) continue; + cloneCount++; + Sheet newSheet = book.cloneSheet(i); + + book.setSheetName(i + cloneCount, s); + stringStringMap.putAll(header); + fillSheet(newSheet, stringStringMap, mapSonDetail.get(s), isExcel2003); + } + book.removeSheetAt(i); + break; + } + } + + book.write(fs); + fs.flush(); + } catch (Exception ioe) { + throw new Exception("读取文件出现错误:" + ioe.getMessage()); + } finally { + IOUtils.closeQuietly(iso); + IOUtils.closeQuietly(fs); + } + + fileName = fileName + "_" + DateUtil.nowDateTimeString() + ".xls"; + return fileName + "@@" + tempFile.getAbsolutePath(); + } + + //读sheet + private static void fillSheet(Sheet sheet, Map header, List> listDetail, String sheetName, boolean isExcel2003) throws Exception { + //String sheetName = sheet.getSheetName(); + int index = sheet.getWorkbook().getSheetIndex(sheet); + sheet.getWorkbook().setSheetName(index, sheetName); + fillSheetEx(sheet, header, listDetail, isExcel2003); + } + + //读sheet + private static void fillSheet(Sheet sheet, Map header, List> listDetail, boolean isExcel2003) throws Exception { + String sheetName = sheet.getSheetName(); + if (sheetName.contains("{#") && sheetName.contains("#}")) { + final int sheetIndex = sheet.getWorkbook().getSheetIndex(sheet); + sheet.getWorkbook().setSheetName(sheetIndex, ExportUtil.myReplaceStrEx(sheetName, "{#", "#}", header)); + } + fillSheetEx(sheet, header, listDetail, isExcel2003); + } + + private static void setCellValue(Cell cell, String value, String org_txt) { + if (org_txt.startsWith("[@expri@]") || org_txt.startsWith("[@i@]")) { + CellStyle cs = cell.getCellStyle(); + if (cs == null) { + cs = cell.getSheet().getWorkbook().createCellStyle(); + cell.setCellStyle(cs); + } + cell.setCellValue(StringUtil.getIntIgnoreErr(value)); + cs.setDataFormat(HSSFDataFormat.getBuiltinFormat("0")); + cell.setCellStyle(cs); + } else if (org_txt.startsWith("[@exprd") || org_txt.startsWith("[@d")) { + String s = org_txt.replace("[@exprd", "").replace("[@d", ""); + int c = StringUtil.getIntIgnoreErr(s.substring(0, s.indexOf("@]"))); + if (c == 0) c = 2; + CellStyle cs = cell.getCellStyle(); + if (cs == null) { + cs = cell.getSheet().getWorkbook().createCellStyle(); + cell.setCellStyle(cs); + } + cell.setCellValue(StringUtil.getDoubleIgnoreErr(value)); + cs.setDataFormat(HSSFDataFormat.getBuiltinFormat("0." + StringUtil.repeatString("0", c))); + cell.setCellStyle(cs); + + } else if (org_txt.startsWith("[@expfml@]")) {//公式格式 + CellStyle cs = cell.getCellStyle(); + if (cs == null) { + cs = cell.getSheet().getWorkbook().createCellStyle(); + cell.setCellStyle(cs); + } + cell.setCellStyle(cs); + cell.setCellFormula("HYPERLINK(\"" + value + "\",\"" + value + "\")"); + } else { + cell.setCellValue(value); + } + } + + private static void fillSheetEx(Sheet sheet, Map header, List> listDetail, boolean isExcel2003) throws Exception { + int totalRows = sheet.getLastRowNum() + 1; + Drawing drawing = sheet.createDrawingPatriarch(); + // excel中的3行,代码中要-1. + for (int i = 0; i < totalRows; i++) { + Row row = sheet.getRow(i); + if (row == null) continue; + if (!isDynRow(row)) {//非动态行 + for (int j = row.getFirstCellNum(), endCell = row.getLastCellNum(); j <= endCell; j++) { + Cell cell = row.getCell(j); + if (null == cell) continue; + if (cell.getCellType() == CellType.STRING) { + String value = cell.getStringCellValue(); + if (StringUtil.isEmpty(value) || !value.contains("[#") || !value.contains("#]")) continue; + String rv = ExportUtil.myReplaceStr(value, header); + if (FileUtil.isImageFromBase64(rv)) { + createPic(sheet, drawing, cell, rv, value, j, i, isExcel2003); + } else { + setCellValue(cell, rv, value); + } + } + } + } else { + if (CommUtil.isEmpty(listDetail)) { + sheet.removeRow(row);//删除行内容 + continue; + } + final int detailSize = listDetail.size(); + + //移动行,插入空行 + final int lastRowNum = sheet.getLastRowNum(); + if (lastRowNum > i) sheet.shiftRows(i + 1, lastRowNum, detailSize - 1, true, false); + + List> list = new ArrayList<>(); + for (int kk = 0; kk < detailSize; kk++) { + Map m = listDetail.get(kk); + Map map = new HashMap<>(m); + map.putAll(header); + list.add(map); + map.put("序号", String.valueOf(kk + 1)); + } + + AutoMergeCols amcs = new AutoMergeCols(); + AutoMergeColInfo mci = null; + for (int j = row.getFirstCellNum(), endCell = row.getLastCellNum(); j <= endCell; j++) { + Cell cell = row.getCell(j); + if (null == cell) continue; + String value = ExcelUtil.getStr(row, j); +// if (cell.getCellType() == Cell.CELL_TYPE_STRING || cell.getCellType() == Cell.CELL_TYPE_FORMULA) { +// String value = cell.getStringCellValue(); +// if (CommUtil.isEmpty(value)) continue; + value = value.replace("[@dyn@]", ""); + boolean automerge = value.contains("[@am@]"); + boolean mergeCell = value.contains("[@mc@]"); + value = value.replace("[@am@]", "").replace("[@mc@]", ""); +// if (!value.contains("[#") || !value.contains("#]")) continue; + + String sValue = ExportUtil.myReplaceStr(value, list.get(0)); +// cell.setCellValue(sValue); + if (FileUtil.isImageFromBase64(sValue)) { + createPic(sheet, drawing, cell, sValue, value, j, i, isExcel2003); + } else { + setCellValue(cell, sValue, value); + } + if (automerge) amcs.addCol(j, i, sValue); + for (int kk = 1; kk < detailSize; kk++) { + boolean isEnd = kk == detailSize - 1; + int end_index = isEnd ? 0 : 1; + Row dr = sheet.getRow(i + kk); + if (dr == null) dr = sheet.createRow(i + kk); + dr.setHeight(row.getHeight()); + + Cell dCell = dr.createCell(j); + + // 风格一样 + dCell.setCellStyle(cell.getCellStyle()); + dCell.setCellType(cell.getCellType()); + sValue = ExportUtil.myReplaceStr(value, list.get(kk)); +// dCell.setCellValue(sValue); + setCellValue(dCell, sValue, value); + if (automerge) {//需要自动合并单元格 + mci = amcs.checkMerge(j, i + kk, sValue, isEnd); + if (mci != null && StringUtil.isNotEmpty(mci.value)) + writeMergeCell(sheet, mci.row, i + kk - end_index, j, j, cell.getCellStyle(), mci.value); + amcs.addCol(j, i + kk, sValue); + } + if (mergeCell) { + writeMergeCell(sheet, i + kk, i + kk, j, getMergeCellEndIndex(j, row), cell.getCellStyle(), sValue); + } + } +// } + } + totalRows += detailSize - 1; + i += detailSize - 1; + } + } + } + + private static int getMergeCellEndIndex(int col, Row row) { + int begin = col; + String value = ""; + while (true) { + begin++; + value = ExcelUtil.getStr(row, begin); + if (!"".equals(value)) { + return begin - 1; + } + if (begin == row.getLastCellNum() - 1) { + return begin; + } + } + } + + public static void createPic(Sheet sheet, Drawing drawing, Cell cell, String value, String org_value, int cell_index, int row_index, boolean isExcel2003) { + try { + BufferedImage bufImg = ImageIO.read(new ByteArrayInputStream(new BASE64Decoder().decodeBuffer(value))); + ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream(); + ImageIO.write(bufImg, "png", byteArrayOut); + byte[] data = byteArrayOut.toByteArray(); + ClientAnchor anchor = null; + if (isExcel2003) + anchor = new HSSFClientAnchor(5, 5, 1000, 250, (short) cell_index, row_index, (short) cell_index, row_index); + else + anchor = new XSSFClientAnchor(0, 0, 1000, 250, (short) cell_index, row_index, (short) cell_index, row_index); + anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE); + drawing.createPicture(anchor, sheet.getWorkbook().addPicture(data, Workbook.PICTURE_TYPE_PNG)); + setCellValue(cell, "", org_value); + } catch (Exception e) { +// UtilLogger.error("插入图片到Excel失败!", e); + System.err.println("插入图片到Excel失败"); + } + } + + public static String createExcelModel(String planName, List head, List detail) throws Exception { + File tempFile = File.createTempFile("xls_", ".xls", new File(System.getProperty("java.io.tmpdir"))); + FileOutputStream fs = new FileOutputStream(tempFile); + try { + HSSFWorkbook wb = new HSSFWorkbook(); + Sheet sheet = wb.createSheet("sheet1"); + Row row = null; + + CellStyle csh = createCellStyle(wb, (short) 16, true, false, HorizontalAlignment.CENTER); + writeMergeCell(sheet, 35, 0, 0, 0, detail.size(), csh, planName); + + int rowIndex = 0; +// if (head != null && head.size() > 0) { +// int i = 0; +// for (String key : head) { +// if (i % 2 == 0) { +// row = createRow(sheet, rowIndex + 1, 20); +// setModelCellValue(row, 0, key); +// setModelCellValue(row, 1, "[#" + key + "#]"); +// rowIndex++; +// } else { +// setModelCellValue(row, 2, key); +// setModelCellValue(row, 3, "[#" + key + "#]"); +// } +// i++; +// } +// } + CellStyle dataStyle_center = createCellStyle(wb, (short) 9, false, true, HorizontalAlignment.CENTER); +// CellStyle dataStyle_left = createCellStyle(wb, (short) 9, false, true, CellStyle.ALIGN_LEFT); + if (detail != null && detail.size() > 0) { + Row title = createRow(sheet, rowIndex + 1, 20); + Row value = createRow(sheet, rowIndex + 2, 20); + Row sum = createRow(sheet, rowIndex + 3, 20); + writeCell(title, 0, dataStyle_center, "序号", ColType.TEXT, false); + writeCell(value, 0, dataStyle_center, "[@dyn@][#序号#]", ColType.TEXT, false); + writeCell(sum, 0, dataStyle_center, "合计", ColType.TEXT, false); + int i = 1; + for (String key : detail) { + writeCell(title, i, dataStyle_center, key, ColType.TEXT, false); + writeCell(value, i, dataStyle_center, "[#" + key + "#]", ColType.TEXT, false); + writeCell(sum, i, dataStyle_center, "[#" + key + "_合计#]", ColType.TEXT, false); + i++; + } + } + + rowIndex += 10; + csh = createCellStyle(wb, (short) 9, false, false, HorizontalAlignment.LEFT); + writeMergeCell(sheet, 20, rowIndex, rowIndex, 0, detail.size(), csh, "支持的变量参数"); + rowIndex++; + writeMergeCell(sheet, 20, rowIndex, rowIndex, 0, detail.size(), csh, "头:" + toText(head)); + rowIndex++; + writeMergeCell(sheet, 20, rowIndex, rowIndex, 0, detail.size(), csh, "明细:" + toText(detail)); + + /*row = createRow(sheet, rowIndex + 4, 20); + writeCell(row, 0, dataStyle_left, "支持的变量参数", ColType.TEXT, false); + row = createRow(sheet, rowIndex + 5, 20); + writeCell(row, 0, dataStyle_left, "头:", ColType.TEXT, false); + writeCell(row, 1, dataStyle_left, toText(head), ColType.TEXT, false); + row = createRow(sheet, rowIndex + 6, 20); + writeCell(row, 0, dataStyle_left, "明细:", ColType.TEXT, false); + writeCell(row, 1, dataStyle_left, toText(detail), ColType.TEXT, false);*/ + + wb.write(fs); + clearCellStyleCache(wb); + fs.flush(); + } catch (Exception ioe) { +// UtilLogger.error("读取文件出现错误:", ioe); + System.err.println("读取文件出现错误"); + throw new Exception("读取文件出现错误:" + ioe.getMessage()); + } finally { + IOUtils.closeQuietly(fs); + } + planName = planName + "_" + DateUtil.nowDateTimeString() + ".xls"; + return planName + "@@" + tempFile.getAbsolutePath(); + } + + private static String toText(List list) { + if (CommUtil.isEmpty(list)) return ""; + StringBuilder sb = new StringBuilder(256); + for (String s : list) { + sb.append(",").append(s); + } + return sb.substring(1); + } + + private static void setModelCellValue(Row row, int cellIndex, String value) throws Exception { + Cell cell = createCell(row, cellIndex); + cell.setCellValue(value); + } + + //是否动态行 + private static boolean isDynRow(Row row) { + final short firstCellNum = row.getFirstCellNum(); + if (firstCellNum < 0) return false; + Cell cell = row.getCell(firstCellNum); + if (cell.getCellType() == CellType.STRING) { + String value = cell.getStringCellValue(); + return !(StringUtil.isEmpty(value) || !value.startsWith("[@dyn@]")); + } + return false; + } + + + //需要自动合并单元格的字段列信息 + private static class AutoMergeColInfo { + private int col = -1;//列索引 + private String key = null;// 合并的值 + private String value = null;//合并的值的format值 + private int row = -1;//开始行索引 + } + + private static class AutoMergeCols { + private Map map = new HashMap<>(); + + private void addCol(int col, int row, String value) { + addCol(col, row, value, value); + } + + private void addCol(int col, int row, String key, String value) { + AutoMergeColInfo colInfo = map.get(col); + if (colInfo == null) { + colInfo = new AutoMergeColInfo(); + map.put(col, colInfo); + } + colInfo.col = col; + if (colInfo.key == null || !colInfo.key.equals(key)) colInfo.row = row; + colInfo.key = key; + colInfo.value = value; + } + + //校验是否需要合并单元格,返回null,不需要,其他则返回合并的开始列信息 + private AutoMergeColInfo checkMerge(int col, int row, String key, boolean isEnd) { + AutoMergeColInfo colInfo = map.get(col); + if (colInfo == null) return null; + if (colInfo.key == null) return null; + if (colInfo.row == row - 1) return null;//上一行开始,表示只有一行是此数据, 不需要考虑合并 + if (isEnd && colInfo.key.equals(key)) return colInfo; + if (colInfo.key.equals(key)) return null; + return colInfo; + } + + private AutoMergeColInfo checkMergeLast(int col) { + AutoMergeColInfo colInfo = map.get(col); + if (colInfo == null) return null; + if (colInfo.value == null) return null; + return colInfo; + } + } + + private static void checkTemplateFile(String modelFile) throws Exception { + if (StringUtil.isEmpty(modelFile) || !FileUtil.isFileExist(modelFile)) throw new Exception("模板文件不存在:" + modelFile); + } + + public static void main(String[] args) throws Exception { + Map map = new HashMap<>(); + map.put("户数-永顺镇", "1"); + map.put("人数-永顺镇", "100"); + map.put("低保金-永顺镇", "1000"); + List> listSon = new ArrayList<>(); + HashMap m = new HashMap<>(); + m.put("区划", "永顺镇"); + m.put("p-人数", "100"); + m.put("p-低保金", "10000"); + listSon.add(m); + + m = new HashMap<>(); + m.put("区划", "宋庄镇"); + m.put("p-人数", "200"); + m.put("p-低保金", "20000"); + listSon.add(m); + + Map>> mapSonDetail = new HashMap<>(); + List> listSonD = new ArrayList<>(); + mapSonDetail.put("永顺镇", listSonD); + m = new HashMap<>(); + m.put("姓名", "永顺镇1"); + m.put("人数", "50"); + m.put("低保金", "5000"); + listSonD.add(m); + + m = new HashMap<>(); + m.put("姓名", "永顺镇2"); + m.put("人数", "40"); + m.put("低保金", "4000"); + listSonD.add(m); + + listSonD = new ArrayList<>(); + mapSonDetail.put("宋庄镇", listSonD); + m = new HashMap<>(); + m.put("姓名", "宋庄镇1"); + m.put("人数", "10"); + m.put("低保金", "1000"); + listSonD.add(m); + + m = new HashMap<>(); + m.put("姓名", "宋庄镇2"); + m.put("人数", "20"); + m.put("低保金", "2000"); + listSonD.add(m); + + // + List> rows = new ArrayList<>(); + HashMap row = new HashMap<>(); + row.put("F1", "宋庄镇2"); + row.put("F2", "20"); + row.put("F3", "2000"); + rows.add(row); + + row = new HashMap<>(); + row.put("F1", "宋庄镇22"); + row.put("F2", "220"); + row.put("F3", "22000"); + rows.add(row); + + row = new HashMap<>(); + row.put("F1", "宋庄镇222"); + row.put("F2", "2220"); + row.put("F3", "222000"); + rows.add(row); + + row = new HashMap<>(); + row.put("F1", "宋庄镇2222"); + row.put("F2", "22220"); + row.put("F3", "2222000"); + rows.add(row); + + String model = "{\"header\":[{\"title\":\"姓名\",\"colspan\":1,\"rowspan\":1,\"row\":0,\"col\":0,\"isLeaf\":true},{\"title\":\"人数\",\"colspan\":1,\"rowspan\":1,\"row\":0,\"col\":1,\"isLeaf\":true},{\"title\":\"低保金\",\"colspan\":1,\"rowspan\":1,\"row\":0,\"col\":2,\"isLeaf\":true}],\"columns\":[{\"field\":\"F1\",\"align\":\"center\",\"title\":\"姓名1\",\"chname\":\"姓名1\"},{\"field\":\"F2\",\"align\":\"center\",\"title\":\"人数1\",\"chname\":\"人数1\"},{\"field\":\"F3\",\"align\":\"center\",\"title\":\"低保金1\",\"chname\":\"低保金1\"}],\"headerRowCount\":1,\"title\":\"收款台列表\",\"rownum_\":0}"; + + String ss = exportToExcel(rows, model); +// String ss = exportToExcelModel(map, listSonD, mapSonDetail, "D:\\jjkj\\test.xlsx", "a"); + System.out.println(ss); + + } +} diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExcelUtil.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExcelUtil.java new file mode 100644 index 0000000..a64fdbf --- /dev/null +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExcelUtil.java @@ -0,0 +1,131 @@ +package cc.smtweb.framework.core.util; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.Row; + +import java.util.ArrayList; +import java.util.List; + +public class ExcelUtil { + private static int xy(int x, int y) { + if (y == 0) return 1; + if (y == 1) return x; + int f = x * x; + for (int i = 0; i < (y - 2); ++i) { + f *= x; + } + return f; + } + + // Z==> 26, AA == > 27 , AB ==> 28 + public static int toColInt(String col) { + col = col.toUpperCase(); + int colv = 0, left = 0; + for (int i = col.length(); i > 0; --i) { + char c = col.charAt(i - 1); + colv += (c - 'A' + 1) * xy(26, left); + ++left; + } + return colv - 1; // excel是从0开始的 + } + + /** + * 数字下标转列 + * @param index + * @return + */ + public static String toColExcel(int index) { + int shang = 0; + int yu = 0; + List list = new ArrayList(); //10进制转26进制 倒序 + do { + shang = index / 26; + yu = index % 26; + index = shang; + list.add(yu); + } while (shang != 0); + StringBuilder sb = new StringBuilder(); + for (int j = list.size() - 1; j >= 0; j--) { + sb.append((char) (list.get(j) + 'A' - (Math.min(j, 1)))); //倒序拼接 序号转字符 非末位 序号减去 1 + } + return sb.toString(); + } + + + // 注意col要大写字母 + public static String getStr(Row row, char col) { + int i = col - 'A'; + return getStr(row, i); + } + + public static String getStr(Row row, int col) { + if(col <0) return ""; + Cell cell = row.getCell(col); + if (null == cell) return ""; + if (cell.getCellType() == CellType.BLANK || cell.getCellType() == CellType.ERROR) { + return ""; + } + // AKzz : cell 是数字时,会报错误, + if (cell.getCellType() == CellType.NUMERIC) { + // 非日期格式 + if (!org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) { + double d = cell.getNumericCellValue(); +// long x = (long) d; + return MathUtil.getDoubleStr(d); + } else { + return cc.smtweb.framework.core.util.DateUtil.toStdDateString(cell.getDateCellValue()); + } + } + + if (cell.getCellType() == CellType.STRING) { + return StringUtil.checkNull(cell.getStringCellValue()); + } + + if (cell.getCellType() == CellType.FORMULA) { + return getFormulaStr(cell); + } + return ""; + } + + // 注意col要大写字母 + public static int getInt(Row row, char col) { + int i = col - 'A'; + return getInt(row, i); + } + + public static int getInt(Row row, int col) { + if(col <0) return 0; + Cell cell = row.getCell(col); + if (null == cell) return 0; + if (cell.getCellType() == CellType.NUMERIC && !org.apache.poi.ss.usermodel.DateUtil.isCellDateFormatted(cell)) { + double d = cell.getNumericCellValue(); + return (int) d; + } else if (cell.getCellType() == CellType.STRING) { + return MathUtil.toint(cell.getStringCellValue(), 0); + } else if (cell.getCellType() == CellType.FORMULA) { + return (int) getFormulaDbl(cell); + } + return 0; + } + + private static String getFormulaStr(Cell cell) { + try { + return StringUtil.checkNull(cell.getStringCellValue()); + } catch (Exception e) { +// UtilLogger.warn("读取数据出错:"+cell, e); + System.err.println("读取数据出错"); + String v = MathUtil.toStdNumberString(cell.getNumericCellValue()); + if (v.endsWith(".00")) v = v.substring(0, v.length() - 3); + return v; + } + } + + private static double getFormulaDbl(Cell cell) { + try { + return cell.getNumericCellValue(); + } catch (Exception e) { + return MathUtil.todouble(cell.getStringCellValue()); + } + } +} diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExportUtil.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExportUtil.java new file mode 100644 index 0000000..b4784f7 --- /dev/null +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/ExportUtil.java @@ -0,0 +1,54 @@ +package cc.smtweb.framework.core.util; + +import java.util.HashMap; +import java.util.Map; + +public class ExportUtil { + //填充变量 + public static String myReplaceStr(String s, Map map) { + boolean isDouble = s.startsWith("[@exprd"); + boolean isInt = s.startsWith("[@expri@]"); + if (isDouble || isInt) { + s = s.substring(s.indexOf("@]") + 2); + String b = "[#", e = "#]", svar; + int n = s.indexOf(b), lb = b.length(); + Map mapvar = new HashMap<>(); + int i = 0; + while (n >= 0) { + svar = "a" + (++i); + String fn = s.substring(n + lb, s.indexOf(e)); + s = s.replace(b + fn + e, svar); + n = s.indexOf(b); + mapvar.put(svar, StringUtil.getDoubleIgnoreErr(map.get(fn))); + } + double d; + try { + d = MathUtil.calcExprMapDouble(s, mapvar); + } catch (Exception e1) { + d = 0.0; +// UtilLogger.error("计算错误:", e1); + } + if (isDouble) { + String v = MathUtil.getAmountStr(d); + v = v.replace(".00", "").replaceAll(",", ""); + if (v.contains(".") && v.endsWith("0")) v = v.substring(0, v.length() - 1); + return v; + } + return MathUtil.toStdNumberString(d, 0); + } else if (s.startsWith("[@d") || s.startsWith("[@i@]")) s = s.substring(s.indexOf("@]") + 2); + else if (s.startsWith("[@expfml@]")) s = s.substring(10); + + return myReplaceStrEx(s, "[#", "#]", map); + } + + //填充变量 + public static String myReplaceStrEx(String s, String b, String e, Map map) { + int n = s.indexOf(b), lb = b.length(); + while (n >= 0) { + String fn = s.substring(n + lb, s.indexOf(e)); + s = s.replace(b + fn + e, StringUtil.checkNull(map.get(fn))); + n = s.indexOf(b); + } + return s; + } +} diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/FileUtil.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/FileUtil.java index 4742429..6bc240a 100644 --- a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/FileUtil.java +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/FileUtil.java @@ -3,6 +3,7 @@ package cc.smtweb.framework.core.util; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; +import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; import javax.imageio.ImageIO; @@ -98,6 +99,24 @@ public class FileUtil { return "data:image/png;base64," + png_base64; } + /** + * 判断是否是图片 + * + * @param base64Str + * @return + */ + public static boolean isImageFromBase64(String base64Str) { + try { + BufferedImage bufImg = ImageIO.read(new ByteArrayInputStream(new BASE64Decoder().decodeBuffer(base64Str))); + if (null == bufImg) { + return false; + } + } catch (Exception e) { + return false; + } + return true; + } + public static boolean copyFile(File oldfile, String newPath, boolean isDelOld) { if (!oldfile.exists()) return true; int byteread; diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/MathUtil.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/MathUtil.java new file mode 100644 index 0000000..34f038b --- /dev/null +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/MathUtil.java @@ -0,0 +1,507 @@ +package cc.smtweb.framework.core.util; + +import org.apache.commons.jexl3.*; +import org.apache.commons.jexl3.internal.Engine; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.util.Map; + +public class MathUtil { + //整数 + private static DecimalFormat dfLng = new DecimalFormat("##############0"); + private static DecimalFormat dfLong = new DecimalFormat("###,###,###,###,##0"); + //一位小数 + private static DecimalFormat df1 = new DecimalFormat("##############0.0"); + private static DecimalFormat df1Format = new DecimalFormat("###,###,###,###,##0.0"); + //两位小数 + private static DecimalFormat df2 = new DecimalFormat("##############0.00"); + private static DecimalFormat df2Format = new DecimalFormat("###,###,###,###,##0.00"); + //四位小数 + private static DecimalFormat df4 = new DecimalFormat("###,###,###,###,##0.0000"); + //六位小数 + private static DecimalFormat df6Number = new DecimalFormat("#######################0.000000"); + private static DecimalFormat df6NumberF = new DecimalFormat("#,###,###,###,###,###,##0.000000"); + + public final static DecimalFormat stdAmountFormat = new DecimalFormat("###,###,###,###,##0.00"); + public final static DecimalFormat stdNumberFormat = new DecimalFormat("#0.00"); + public final static String DEF_NUM_TEN_THOUSAND = "10000";//万 + public static final double MAX_VALUE = 9999999999999.99D; + public static final double MIN_VALUE = -9999999999999.99D; + + private final static BigDecimal ONE_BIG = new BigDecimal(1.00D); + private static final String UNIT = "万仟佰拾亿仟佰拾万仟佰拾元角分"; + private static final String DIGIT = "零壹贰叁肆伍陆柒捌玖"; + + /** + * 4舍5入double,2位小数 + */ + public static double roundDouble(double src) { + return roundDouble(src, 2); + } + + /** + * 4舍5入double,N 位小数 + * + * @param src + * @param scale 小数位数 + * @return + */ + public static double roundDouble(Object src, int scale) { + if (src == null) return 0.0; + String v = src.toString(); + if (StringUtil.isEmpty(v)) return 0.0; + if (scale < 0) scale = 2; + + BigDecimal src_b = new BigDecimal(v); + BigDecimal src_v = src_b.divide(ONE_BIG, scale + 2, BigDecimal.ROUND_HALF_UP);// 4舍5入 + src_v = src_v.divide(ONE_BIG, scale, BigDecimal.ROUND_HALF_UP);// 4舍5入 + return src_v.doubleValue(); + } + + /** + * 舍位处理,原生floor有坑,部分浮点数记录为.99999999999形式,导致floor结果错误 + * + * @param d + * @return + */ + public static double floor(double d) { + return Math.floor(MathUtil.roundDouble(d, 2)); + } + + /** + * 比较两Double是否相等,将会吧他们专程BigDecimal进行比较; + * + * @param src double1 + * @param tag double2 + * @return src > tag 返回1, src < tag 返回-1, 否则返回0 + */ + public static int compare(double src, double tag) { + BigDecimal src_b = new BigDecimal(src); + BigDecimal src_v = src_b.divide(ONE_BIG, 2, BigDecimal.ROUND_HALF_UP);// 4舍5入 + + BigDecimal tag_b = new BigDecimal(tag); + BigDecimal tag_v = tag_b.divide(ONE_BIG, 2, BigDecimal.ROUND_HALF_UP);// 4舍5入 + + return src_v.compareTo(tag_v); + } + + /** + * 自动过滤金额中的逗号转换为double,如果出错,则返回0 + * + * @param s 源串,可能为带逗号的金额串; + * @return double + */ + public static Double toDouble(String s) { + return todouble(s); + } + + /** + * 自动过滤金额中的逗号转换为double,如果出错,则返回0 + * + * @param s 源串,可能为带逗号的金额串; + * @return double + */ + public static double todouble(String s) { + try { + return Double.parseDouble(s.replaceAll(",", "")); + } catch (Exception e) { + return 0.00; + } + } + + /** + * 获取double,主要过滤d为null的情况; + * + * @param d Double对象; + * @return double + */ + public static double todouble(Double d) { + if (d == null) return 0.0d; + return d.doubleValue(); + } + + /** + * 自动过滤金额中的逗号转换为float,如果出错,则返回0 + * + * @param s 源串,可能为带逗号的金额串; + * @return Float + */ + public static Float toFloat(String s) { + return tofloat(s); + } + + /** + * 自动过滤金额中的逗号转换为float,如果出错,则返回0 + * + * @param s 源串,可能为带逗号的金额串; + * @return Float + */ + public static float tofloat(String s) { + try { + return Float.parseFloat(s.replaceAll(",", "")); + } catch (Exception e) { + return 0.0f; + } + } + + public static long tolong(String src, long defaultvalue) { + try { + return Long.parseLong(src); + } catch (Exception e) { + return defaultvalue; + } + } + + public static int toint(String src, int defaultvalue) { + try { + return Integer.parseInt(src); + } catch (Exception e) { + return defaultvalue; + } + } + + /** + * 考虑使用中的精度,判断一个Value是否>0,实际是>0.00001 + * + * @param value double类型 + * @return boolean + */ + public static boolean isBigThanZero(double value) { + return (value > 0.00001); + } + + /** + * 考虑使用中的精度,判断一个Value是否>0,实际是>0.00001 + * + * @param value String类型 + * @return boolean + */ + public static boolean isBigThanZero(String value) { + return !StringUtil.isEmpty(value) && isBigThanZero(toDouble(value)); + } + + /** + * 考虑使用中的精度,判断一个Value是否=0,实际是给出一个值范围。 + * + * @param value double类型 + * @return boolean + */ + public static boolean isEqualsZero(double value) { + return (-0.00001 < value && value < 0.00001); + } + + /** + * 考虑使用中的精度,判断一个Value是否=0,实际是给出一个值范围。 + * + * @param value String类型 + * @return boolean + */ + public static boolean isEqualsZero(String value) { + return StringUtil.isEmpty(value) || isEqualsZero(toDouble(value)); + } + + /** + * 是否是负数 + * + * @param db_val 要判断的double + * @return 负数则返回true; + */ + public static boolean isNegative(double db_val) { + return (compare(db_val, 0.00D) == -1); + } + + /** + * 是否是正数 + * + * @param db_val 要判断的double + * @return 正数则返回true; + */ + public static boolean isPlus(double db_val) { + return (compare(db_val, 0.00D) == 1); + } + + /** + * 得到金额字符串,保持小数点2位 + * + * @param db 将double转换为金额字符串; + * @return 金额字符串#0.00; + */ + public static String toStdNumberString(double db) { + try { + return stdNumberFormat.format(db); + } catch (Exception e) { + return "0.00"; + } + } + + public static String toStdNumberStringEx(double db) { + try { + if (compare(db, -1d) == 0) return "-"; + return stdNumberFormat.format(db); + } catch (Exception e) { + return "0.00"; + } + } + + /** + * 将金额格式字符串,如23,333,093.01 去掉逗号 + * + * @param s 金额串 + * @return String 去掉逗号后的串,如果amount为空,则返回0.00 + */ + public static String toStdNumberString(String s) { + if (StringUtil.isEmpty(s)) + return "0.00"; + return stdNumberFormat.format(MathUtil.todouble(s)); + } + + /** + * 将数据转换为两位小数的数字格式; + * + * @param d 数据 + * @param isZeroToEmpty 如果未0,是否返回“”; + * @return 两位小数的字符串; + */ + public static String toStdNumberString(double d, boolean isZeroToEmpty) { + if (isEqualsZero(d)) { + return isZeroToEmpty ? "" : "0.00"; + } + return stdNumberFormat.format(d); + } + + public static String toStdNumberString(String s, boolean isZeroToEmpty) { + return toStdNumberString(MathUtil.todouble(s), isZeroToEmpty); + } + + public static String toStdNumberString(double d, int scale) { + DecimalFormat dfn = null; + if (scale == 1) dfn = df1Format; + if (scale == 2) dfn = df2Format; + else if (scale == 4) dfn = df4; + else if (scale == 6) dfn = df6NumberF; + else if (scale <= 0) dfn = dfLong; + else { + StringBuilder sb = new StringBuilder("###,###,###,###,##0."); + for (int i = 0; i < scale; i++) sb.append("0"); + dfn = new DecimalFormat(sb.toString()); + } + return dfn.format(d); + } + + /** + * 将数字乘100,保留小数点后两位, 然后后面添加% + * + * @param d 值 + * @param isZeroToEmpty,如果值为0,是否返回空; + * @return 字符串; + */ + public static String toStdPercentNumberStr(double d, boolean isZeroToEmpty) { + if (d > -0.00000000001 && d < 0.00000000001) { + return isZeroToEmpty ? "" : "0.00%"; + } + return toStdNumberString(d * 100) + "%"; + } + + + public static String toStdAmountString(double d) { + return toStdAmountString(d, false); + } + + /** + * 将数据转换为两位小数的金额格式,带逗号; + * + * @param d 数据 + * @param isZeroToEmpty 如果未0,是否返回“”; + * @return 金额格式的字符串; + */ + public static String toStdAmountString(double d, boolean isZeroToEmpty) { + if (isEqualsZero(d)) { + return isZeroToEmpty ? "" : "0.00"; + } + return stdAmountFormat.format(d); + } + + public static String toStdAmountString(String s) { + return toStdAmountString(MathUtil.todouble(s), false); + } + + public static String toStdAmountString(String s, boolean isZeroToEmpty) { + return toStdAmountString(MathUtil.todouble(s), isZeroToEmpty); + } + + + /** + * 将小写金额转换为人民币大写金额 + * + * @param s 金额格式的串 + * @return String 转换结果 + */ + public static String toCapsAmountString(String s) { + if (StringUtil.isEmpty(s)) return ""; + return toCapsAmountString(todouble(s)); + } + + /** + * 将小写金额转换为人民币大写金额 + * + * @param v double + * @return String 转换结果 + */ + public static String toCapsAmountString(double v) { + if (v < MIN_VALUE || v > MAX_VALUE) return "参数非法!"; + + boolean negative = isNegative(v); + + if (negative) v = Math.abs(v); + long l = Math.round(v * 100); + if (l == 0) return "零元整"; + + String strValue = String.valueOf(l); + // i用来控制数 + int i = 0; + // j用来控制单位 + int j = UNIT.length() - strValue.length(); + StringBuilder rs = new StringBuilder(32); + boolean isZero = false; + for (; i < strValue.length(); i++, j++) { + char ch = strValue.charAt(i); + if (ch == '0') { + isZero = true; + if (UNIT.charAt(j) == '亿' || UNIT.charAt(j) == '万' || UNIT.charAt(j) == '元') { + rs.append(UNIT.charAt(j)); + isZero = false; + } + } else { + if (isZero) { + rs.append('零'); + isZero = false; + } + rs.append(DIGIT.charAt(ch - '0')).append(UNIT.charAt(j)); + } + } + if (rs.charAt(rs.length() - 1) != '分') + rs.append('整'); + + i = rs.indexOf("亿万"); + if (i > 0) rs.delete(i + 1, i + 2); // i+1 ->万 + + if (negative) + return rs.insert(0, '负').toString(); + else + return rs.toString(); + } + + /** + * 返回0 到 maxvalue的随机数 + * + * @param maxvalue 随机数的最大值 + * @return int + */ + public static int rnd(int maxvalue) { + return (int) (Math.random() * (maxvalue + 1)); + } + + + public static double chkDbNull(Double v) { + return v == null ? 0 : v; + } + + public static double max(double d1, double d2) { + return compare(d1, d2) < 0 ? d2 : d1; + } + + public static double min(double d1, double d2) { + return compare(d1, d2) < 0 ? d1 : d2; + } + + /** + * double 去尾法 + * + * @param src 待处理数据 + * @param scale 保留小数位数 + * @return + */ + public static double cutDouble(double src, int scale) { + String v = toStdNumberString(src, 6);//先到6位小数,再去计算,否则容易出错,如8.3成8.29999999999,舍位就成了8.29了 + DecimalFormat formater = new DecimalFormat(); + formater.setMaximumFractionDigits(scale); + formater.setGroupingSize(0); + formater.setRoundingMode(RoundingMode.FLOOR); + return new BigDecimal(formater.format(toDouble(v))).doubleValue(); + } + + public static String getAmountStr(double amount) { + return df2Format.format(amount); + } + + //数字转字符串,如无小数,则去掉.00 + public static String getDoubleStr(double d) { + return getAmountStr(d).replaceAll(",", "").replaceAll("\\.00", ""); + } + + /** + * double 进位法 + * + * @param src 待处理数据 + * @param scale 保留小数位数 + * @return + */ + public static double upDouble(double src, int scale) { + String v = toStdNumberString(src, 6);//先到6位小数,再去计算 + DecimalFormat formater = new DecimalFormat(); + formater.setMaximumFractionDigits(scale); + formater.setGroupingSize(0); + formater.setRoundingMode(RoundingMode.UP); + return new BigDecimal(formater.format(toDouble(v))).doubleValue(); + } + + /** + * 计算公式 参数以Map方式传入 + * + * @param expr 表达式 + * @param mapVar 变量 + * @return 计算结果 + */ + public static Object calcExprMapObject(String expr, Map mapVar) { + if (StringUtil.isEmpty(expr)) return ""; + JexlContext jc = new MapContext(); + jc.set("Math", Math.class); + jc.set("UtilPub", StringUtil.class); + for (String k : mapVar.keySet()) jc.set(k, mapVar.get(k)); + JexlExpression e = new Engine().createExpression(expr); + return e.evaluate(jc); + } + + /** + * 计算公式,参数以数组方式传入,key、value交替 + * + * @param expr 表达式 + * @param vars 变量 + * @return + */ + public static Object calcExprObjectEx(String expr, Object... vars) { + if (StringUtil.isEmpty(expr)) return ""; + JexlContext jc = new MapContext(); + jc.set("Math", Math.class); + jc.set("UtilPub", StringUtil.class); + for (int i = 0, len = vars.length; i < len; ) { + jc.set((String) vars[i++], vars[i++]); + } + JexlExpression e = new Engine().createExpression(expr); + return e.evaluate(jc); + } + + public static double calcExprMapDouble(String expr, Map mapVar) { + Object o = calcExprMapObject(expr, mapVar); + if (o != null) return StringUtil.getDoubleIgnoreErr(o.toString()); + return 0.0; + } + + public static double calcExprDoubleEx(String expr, Object... vars) { + Object o = calcExprObjectEx(expr, vars); + if (o != null) return StringUtil.getDoubleIgnoreErr(o.toString()); + return 0.0; + } +} diff --git a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/StringUtil.java b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/StringUtil.java index 16a108f..b24cb8f 100644 --- a/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/StringUtil.java +++ b/smtweb-framework/core/src/main/java/cc/smtweb/framework/core/util/StringUtil.java @@ -5,6 +5,7 @@ import org.apache.commons.lang3.StringUtils; import java.nio.charset.StandardCharsets; import java.text.Collator; +import java.text.DecimalFormat; import java.util.*; /** @@ -15,6 +16,8 @@ import java.util.*; */ @SuppressWarnings("UnusedDeclaration") public class StringUtil { + private static DecimalFormat df2Format = new DecimalFormat("###,###,###,###,##0.00"); + private static Collator chineseCollator = Collator.getInstance(Locale.CHINA); public static boolean isEmpty(String str) { @@ -182,6 +185,52 @@ public class StringUtil { } /** + * 获取浮点数(有错误默认为0),可以识别金额中的逗号格式 + * + * @param str 带转换的字符串 + * @return 浮点数 + */ + public static double getDoubleIgnoreErr(String str) { + if (str == null) + return 0.0; + str = str.trim(); + if (str.equals("")) + return 0.0; + str = str.replaceAll(",", "").replaceAll(",", ""); + try { + return Double.valueOf(str); + } catch (Exception e) { + return 0.0; + } + } + + /** + * 得到int 获取转换的int值,有错返回0 + * + * @param str 带转换的字符串 + * @return int + */ + public static int getIntIgnoreErr(String str) { + return getIntIgnoreErr(str, 0); + } + + public static int getIntIgnoreErr(String str, int defValue) { + if (str == null) + return defValue; + str = str.trim(); + if (str.equals("")) + return defValue; + str = str.replaceAll(",", "").replaceAll(",", ""); + if (str.contains(".")) + str = str.substring(0, str.indexOf('.')); + try { + return Integer.valueOf(str); + } catch (Exception e) { + return defValue; + } + } + + /** * 取得重复字串 * * @param repeatString 重复字串