Explorar o código

客户端-质检下载模板接口调整

LHB hai 2 meses
pai
achega
7669b84691

+ 596 - 0
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/HtmlTableToExcelConverter.java

@@ -0,0 +1,596 @@
+package org.springblade.manager.controller;
+
+import com.aspose.cells.LoadFormat;
+import com.aspose.cells.LoadOptions;
+import com.aspose.cells.SaveFormat;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.ss.util.RegionUtil;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+import org.springblade.core.tool.utils.IoUtil;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.manager.utils.FileUtils;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+public class HtmlTableToExcelConverter {
+
+//    public static void main(String[] args) throws Exception {
+//        String html = "D:\\tools\\html\\1892816666778140672.html";
+//
+//
+//        InputStream inputStreamByUrl = FileUtils.getInputStreamByUrl(html);
+//        String htmlString = IoUtil.readToString(inputStreamByUrl);
+//
+//        convertHtmlTableToExcel(htmlString, "D:\\tools\\html\\123.xlsx");
+//
+//
+//    }
+
+    public static Workbook convertHtmlTableToExcel(String html) throws Exception {
+        Document doc = Jsoup.parse(html);
+        Element table = doc.select("table").first();
+
+        // 创建Excel工作簿
+        Workbook workbook = new XSSFWorkbook();
+        Sheet sheet = workbook.createSheet("Sheet1");
+
+        // 设置默认列宽(字符宽度)
+        sheet.setDefaultColumnWidth(10);
+
+        // 存储列宽(单位:字符)
+        Map<Integer, Float> columnWidths = new HashMap<>();
+
+        // 样式缓存
+        Map<String, CellStyle> styleCache = new HashMap<>();
+        Map<String, Font> fontCache = new HashMap<>();
+        // 预解析CSS样式
+        Map<String, String> cssRules = parseCssRules(doc);
+
+        // 合并区域跟踪列表
+        List<CellRangeAddress> mergedRegions = new ArrayList<>();
+        // 单元格占用跟踪器
+        Map<Integer, Set<Integer>> occupiedCells = new HashMap<>();
+
+        int excelRowNum = 0;
+        for (Element tr : table.select("tr")) {
+            // 跳过空行
+            if (tr.children().isEmpty()) {
+                excelRowNum++;
+                continue;
+            }
+
+            // 创建行并设置默认行高(比默认值小)
+            Row excelRow = sheet.createRow(excelRowNum);
+
+
+            // 初始化当前行占用集合
+            occupiedCells.putIfAbsent(excelRowNum, new HashSet<>());
+
+            int excelColNum = 0;
+
+
+            //是否设置行高
+            Boolean isHeight = false;
+
+            for (Element td : tr.select("td")) {
+
+                //设置行高
+                String style1 = td.attr("style");
+                if (!isHeight && StringUtil.isNotBlank(style1)) {
+                    List<String> collect = Arrays.stream(style1.split(";")).collect(Collectors.toList());
+                    HashMap<String, String> map = new HashMap<>();
+                    collect.forEach(s -> {
+                        map.put(s.split(":")[0], s.split(":")[1]);
+                    });
+
+                    String height = map.get("height");
+                    Float rowHeight = 16f; // 默认16点
+                    if (StringUtil.isNotBlank(height)) {
+                        isHeight = true;
+                        height = height.replace("px", "");
+                        try {
+                            Float px = Float.valueOf(height);
+                            rowHeight = px * 0.75f; // 像素转点 (1px ≈ 0.75pt)
+                        } catch (Exception ignored) {
+                            ignored.printStackTrace();
+                        }
+
+                        excelRow.setHeightInPoints(rowHeight);
+                    }
+                }
+
+
+                // 跳过已被占用的单元格
+                while (isCellOccupied(occupiedCells, excelRowNum, excelColNum)) {
+                    excelColNum++;
+                }
+
+                // 处理列跨度和行跨度
+                int colspan = getSpan(td, "colspan");
+                int rowspan = getSpan(td, "rowspan");
+
+                // 获取单元格内容
+                String cellText = extractCellText(td);
+
+                // 创建单元格
+                Cell cell = excelRow.createCell(excelColNum);
+                cell.setCellValue(cellText);
+
+                // 应用样式
+                String styleKey = getCellStyleKey(td, cssRules);
+                if (!styleCache.containsKey(styleKey)) {
+                    CellStyle style = createCellStyle(workbook, td, cssRules, fontCache);
+                    styleCache.put(styleKey, style);
+
+                    // 检查是否需要自动换行
+                    if (shouldWrapText(td, cssRules,cellText)) {
+                        style.setWrapText(true);
+                    }
+                }
+                cell.setCellStyle(styleCache.get(styleKey));
+
+                // 记录列宽(优先使用HTML中的宽度)
+                if (td.hasAttr("width")) {
+                    String widthStr = td.attr("width").replace("px", "");
+                    try {
+                        float px = Float.parseFloat(widthStr);
+                        float charWidth = px / 7f; // 像素转字符宽度 (1字符≈7px)
+
+                        // 考虑跨列情况:总宽度分配到各列
+                        for (int i = 0; i < colspan; i++) {
+                            int colIdx = excelColNum + i;
+                            float perColWidth = charWidth / colspan;
+
+                            // 取最大宽度作为列宽
+                            columnWidths.putIfAbsent(colIdx, 0f);
+                            if (perColWidth > columnWidths.get(colIdx)) {
+                                columnWidths.put(colIdx, perColWidth);
+                            }
+                        }
+                    } catch (NumberFormatException ignored) {
+                    }
+                }
+
+                // 标记当前单元格占据的所有位置
+                markCellsAsOccupied(occupiedCells, excelRowNum, excelColNum, rowspan, colspan);
+
+                // 创建合并区域
+                if (colspan > 1 || rowspan > 1) {
+                    CellRangeAddress region = new CellRangeAddress(
+                            excelRowNum,
+                            excelRowNum + rowspan - 1,
+                            excelColNum,
+                            excelColNum + colspan - 1
+                    );
+
+                    // 检查是否与现有区域重叠
+                    if (!isOverlapping(mergedRegions, region)) {
+                        sheet.addMergedRegion(region);
+                        mergedRegions.add(region);
+                    }
+                }
+
+                excelColNum += colspan;
+            }
+            excelRowNum++;
+        }
+        // 修复合并单元格边框问题
+        fixMergedRegionBorders(workbook, sheet, mergedRegions);
+
+        // 应用列宽
+        for (Map.Entry<Integer, Float> entry : columnWidths.entrySet()) {
+            int colIdx = entry.getKey();
+            float width = entry.getValue();
+
+            // 限制列宽范围(0.5-100字符)
+            if (width < 0.5f) width = 0.5f;
+            if (width > 100f) width = 100f;
+
+            // 转换字符宽度为Excel单位(1字符=256单位)
+            sheet.setColumnWidth(colIdx, (int) (width * 256));
+        }
+
+        // 自动调整未设置宽度的列
+        int maxColumn = columnWidths.keySet().stream().max(Integer::compare).orElse(0) + 1;
+        for (int i = 0; i < maxColumn; i++) {
+            if (!columnWidths.containsKey(i)) {
+                sheet.autoSizeColumn(i);
+                // 限制自动调整的最大宽度
+                int currentWidth = sheet.getColumnWidth(i);
+                if (currentWidth > 30 * 256) {
+                    sheet.setColumnWidth(i, 30 * 256);
+                }
+            }
+        }
+
+        // 调整行高以适应换行文本
+        adjustRowHeights(sheet);
+
+        // 写入文件
+//        try (FileOutputStream fos = new FileOutputStream(outputPath)) {
+//            workbook.write(fos);
+//        }
+//        workbook.close();
+
+        return workbook;
+    }
+
+    // 修复合并单元格边框问题
+    private static void fixMergedRegionBorders(Workbook workbook, Sheet sheet, List<CellRangeAddress> mergedRegions) {
+        for (CellRangeAddress region : mergedRegions) {
+            // 设置合并区域的外边框
+            RegionUtil.setBorderTop(BorderStyle.THIN, region, sheet);
+            RegionUtil.setBorderRight(BorderStyle.THIN, region, sheet);
+            RegionUtil.setBorderBottom(BorderStyle.THIN, region, sheet);
+            RegionUtil.setBorderLeft(BorderStyle.THIN, region, sheet);
+
+            RegionUtil.setTopBorderColor(IndexedColors.BLACK.getIndex(), region, sheet);
+            RegionUtil.setRightBorderColor(IndexedColors.BLACK.getIndex(), region, sheet);
+            RegionUtil.setBottomBorderColor(IndexedColors.BLACK.getIndex(), region, sheet);
+            RegionUtil.setLeftBorderColor(IndexedColors.BLACK.getIndex(), region, sheet);
+
+            // 特殊处理:确保内部单元格边框完整
+            int firstRow = region.getFirstRow();
+            int lastRow = region.getLastRow();
+            int firstCol = region.getFirstColumn();
+            int lastCol = region.getLastColumn();
+
+            // 对于多行多列的合并区域,需要额外设置内部边框
+            if (lastRow - firstRow > 0 || lastCol - firstCol > 0) {
+                // 设置内部水平边框
+                for (int row = firstRow; row <= lastRow; row++) {
+                    Row excelRow = sheet.getRow(row);
+                    if (excelRow == null) continue;
+
+                    for (int col = firstCol; col <= lastCol; col++) {
+                        Cell cell = excelRow.getCell(col);
+                        if (cell == null) continue;
+
+                        CellStyle style = cell.getCellStyle();
+
+                        // 设置顶部边框(如果是区域的第一行)
+                        if (row == firstRow) {
+                            style.setBorderTop(BorderStyle.THIN);
+                        }
+
+                        // 设置底部边框(如果是区域的最后一行)
+                        if (row == lastRow) {
+                            style.setBorderBottom(BorderStyle.THIN);
+                        }
+
+                        // 设置左侧边框(如果是区域的第一列)
+                        if (col == firstCol) {
+                            style.setBorderLeft(BorderStyle.THIN);
+                        }
+
+                        // 设置右侧边框(如果是区域的最后一列)
+                        if (col == lastCol) {
+                            style.setBorderRight(BorderStyle.THIN);
+                        }
+
+                        // 设置内部边框
+                        if (row < lastRow) {
+                            style.setBorderBottom(BorderStyle.THIN);
+                        }
+                        if (col < lastCol) {
+                            style.setBorderRight(BorderStyle.THIN);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+
+    // 新增方法:检查是否需要自动换行
+    private static boolean shouldWrapText(Element td, Map<String, String> cssRules,String cellText) {
+        // 1. 检查内联样式
+        String style = td.attr("style");
+        if (style != null && style.toLowerCase().contains("overflow-wrap") ||
+                style != null && style.toLowerCase().contains("word-wrap") ||
+                style != null && style.toLowerCase().contains("white-space") ||
+                style != null && style.toLowerCase().contains("line-break") ||
+                style != null && style.toLowerCase().contains("hyphens")) {
+            return true;
+        }
+
+        // 2. 检查CSS类样式
+        String classes = td.attr("class");
+        for (String cls : classes.split(" ")) {
+            String selector = "." + cls;
+            if (cssRules.containsKey(selector)) {
+                String css = cssRules.get(selector).toLowerCase();
+                if (css.contains("overflow-wrap") ||
+                        css.contains("word-wrap") ||
+                        css.contains("white-space") ||
+                        css.contains("line-break") ||
+                        css.contains("hyphens")) {
+                    return true;
+                }
+            }
+        }
+
+        // 3. 默认不自动换行(除非内容中有换行符)
+        return cellText.contains("\n");
+    }
+
+
+    // 新增方法:调整行高以适应换行文本
+    private static void adjustRowHeights(Sheet sheet) {
+        for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) {
+            Row row = sheet.getRow(rowNum);
+            if (row == null) continue;
+
+            // 计算该行所需的最大行高
+            int maxLines = 1;
+            for (Cell cell : row) {
+                if (cell.getCellStyle().getWrapText()) {
+                    String text = cell.getStringCellValue();
+                    if (text != null) {
+                        int lines = text.split("\n").length;
+                        if (lines > maxLines) maxLines = lines;
+                    }
+                }
+            }
+
+            // 根据行数调整行高(每行约16点)
+            if (maxLines > 1) {
+                float newHeight = row.getHeightInPoints() * (maxLines * 0.8f);
+                row.setHeightInPoints(Math.max(row.getHeightInPoints(), newHeight));
+            }
+        }
+    }
+
+
+
+    // 辅助方法:检查单元格是否被占用
+    private static boolean isCellOccupied(Map<Integer, Set<Integer>> occupiedCells, int row, int col) {
+        if (!occupiedCells.containsKey(row)) {
+            return false;
+        }
+        return occupiedCells.get(row).contains(col);
+    }
+
+
+    // 辅助方法:标记占用单元格
+    private static void markCellsAsOccupied(Map<Integer, Set<Integer>> occupiedCells,
+                                            int startRow, int startCol,
+                                            int rowspan, int colspan) {
+        for (int r = startRow; r < startRow + rowspan; r++) {
+            for (int c = startCol; c < startCol + colspan; c++) {
+                occupiedCells.putIfAbsent(r, new HashSet<>());
+                occupiedCells.get(r).add(c);
+            }
+        }
+    }
+
+    // 辅助方法:检查区域重叠
+    private static boolean isOverlapping(List<CellRangeAddress> existingRegions, CellRangeAddress newRegion) {
+        for (CellRangeAddress region : existingRegions) {
+            if (region.intersects(newRegion)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // 辅助方法:获取跨行/跨列值
+    private static int getSpan(Element element, String attr) {
+        String span = element.attr(attr);
+        return span.isEmpty() ? 1 : Integer.parseInt(span);
+    }
+
+    // 辅助方法:提取单元格文本内容
+    private static String extractCellText(Element td) {
+        // 优先获取直接文本内容
+        if (!td.ownText().isEmpty()) {
+            return td.ownText().trim();
+        }
+        return "";
+    }
+
+    // 辅助方法:解析CSS规则
+    private static Map<String, String> parseCssRules(Document doc) {
+        Map<String, String> rules = new HashMap<>();
+        Elements styles = doc.select("style");
+
+        for (Element style : styles) {
+            String css = style.data();
+            // 简化解析:提取类样式
+            Pattern pattern = Pattern.compile("(\\.[^{]+)\\s*\\{([^}]+)\\}");
+            Matcher matcher = pattern.matcher(css);
+
+            while (matcher.find()) {
+                String selector = matcher.group(1).trim();
+                String properties = matcher.group(2).trim();
+                rules.put(selector, properties);
+            }
+        }
+        return rules;
+    }
+
+    // 辅助方法:获取单元格样式键
+    private static String getCellStyleKey(Element td, Map<String, String> cssRules) {
+        StringBuilder key = new StringBuilder();
+        String classes = td.attr("class");
+
+        for (String cls : classes.split(" ")) {
+            String selector = "." + cls;
+            if (cssRules.containsKey(selector)) {
+                key.append(cssRules.get(selector)).append(";");
+            }
+        }
+
+        key.append(td.attr("style"));
+        return key.toString();
+    }
+
+    // 修改后的辅助方法:创建Excel单元格样式
+    private static CellStyle createCellStyle(Workbook workbook, Element td,
+                                             Map<String, String> cssRules,
+                                             Map<String, Font> fontCache) {
+        CellStyle style = workbook.createCellStyle();
+        Font font = workbook.createFont();
+
+        // 设置默认字体(避免过大)
+        font.setFontName("宋体");
+
+        String classes = td.attr("class");
+
+        // 应用CSS类样式
+        for (String cls : classes.split(" ")) {
+            String selector = "." + cls;
+            if (cssRules.containsKey(selector)) {
+//                applyCssStyle(style, font, cssRules.get(selector));
+            }
+        }
+
+        // 应用内联样式
+        applyCssStyle(style, font, td.attr("style"));
+
+        // 限制最大字体大小
+        if (font.getFontHeightInPoints() > 16) {
+            font.setFontHeightInPoints((short) 16);
+        }
+
+        // 字体缓存
+        String fontKey = font.getFontName() + font.getFontHeightInPoints() + font.getBold();
+        if (!fontCache.containsKey(fontKey)) {
+            Font newFont = workbook.createFont();
+            newFont.setFontName(font.getFontName());
+            newFont.setFontHeightInPoints(font.getFontHeightInPoints());
+            newFont.setBold(font.getBold());
+            fontCache.put(fontKey, newFont);
+        }
+        style.setFont(fontCache.get(fontKey));
+
+        // 设置边框(细线)
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+        style.setTopBorderColor(IndexedColors.BLACK.getIndex());
+        style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
+        style.setLeftBorderColor(IndexedColors.BLACK.getIndex());
+        style.setRightBorderColor(IndexedColors.BLACK.getIndex());
+
+        return style;
+    }
+
+    // 辅助方法:应用CSS样式
+    private static void applyCssStyle(CellStyle style, Font font, String css) {
+        if (css == null || css.isEmpty()) return;
+
+        // 解析CSS属性
+        String[] properties = css.split(";");
+        for (String prop : properties) {
+            String[] parts = prop.split(":");
+            if (parts.length < 2) continue;
+
+            String key = parts[0].trim();
+            String value = parts[1].trim();
+
+            switch (key) {
+                case "background-color":
+//                    setBackgroundColor(style, value);
+                    break;
+                case "font-family":
+                    font.setFontName(value);
+                    break;
+                case "font-size":
+                    setFontSize(font, value);
+                    break;
+                case "font-weight":
+                    if ("bold".equalsIgnoreCase(value)) {
+                        font.setBold(true);
+                    }
+                    break;
+                case "text-align":
+                    setAlignment(style, value);
+                    break;
+                case "vertical-align":
+                    setVerticalAlignment(style, value);
+                    break;
+            }
+        }
+    }
+
+    // 辅助方法:设置背景色
+    private static void setBackgroundColor(CellStyle style, String color) {
+        // 简化处理:只处理rgb格式
+        if (color.startsWith("rgb(")) {
+            String[] rgb = color.substring(4, color.length() - 1).split(",");
+            if (rgb.length == 3) {
+                // 实际应用中应使用IndexedColors或自定义颜色
+                style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+                style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+            }
+        }
+    }
+
+    // 修改后的辅助方法:设置字体大小(添加上限)
+    private static void setFontSize(Font font, String size) {
+        if (size == null || size.isEmpty()) return;
+
+        if (size.endsWith("pt")) {
+            try {
+                float pt = Float.parseFloat(size.substring(0, size.length() - 2));
+                // 限制最大字体大小为16pt
+                if (pt > 16) pt = 16;
+                font.setFontHeightInPoints((short) pt);
+            } catch (NumberFormatException ignored) {
+            }
+        } else if (size.endsWith("px")) {
+            try {
+                float px = Float.parseFloat(size.substring(0, size.length() - 2));
+                // 将像素转换为点 (1pt ≈ 1.33px)
+                float pt = px / 1.33f;
+                if (pt > 16) pt = 16;
+                font.setFontHeightInPoints((short) pt);
+            } catch (NumberFormatException ignored) {
+            }
+        }
+    }
+
+    // 辅助方法:设置水平对齐
+    private static void setAlignment(CellStyle style, String align) {
+        switch (align.toLowerCase()) {
+            case "center":
+                style.setAlignment(HorizontalAlignment.CENTER);
+                break;
+            case "left":
+                style.setAlignment(HorizontalAlignment.LEFT);
+                break;
+            case "right":
+                style.setAlignment(HorizontalAlignment.RIGHT);
+                break;
+            case "justify":
+                style.setAlignment(HorizontalAlignment.JUSTIFY);
+                break;
+        }
+    }
+
+    // 辅助方法:设置垂直对齐
+    private static void setVerticalAlignment(CellStyle style, String align) {
+        switch (align.toLowerCase()) {
+            case "center":
+                style.setVerticalAlignment(VerticalAlignment.CENTER);
+                break;
+            case "top":
+                style.setVerticalAlignment(VerticalAlignment.TOP);
+                break;
+            case "bottom":
+                style.setVerticalAlignment(VerticalAlignment.BOTTOM);
+                break;
+        }
+    }
+}

+ 201 - 186
blade-service/blade-manager/src/main/java/org/springblade/manager/controller/WbsTreeContractController.java

@@ -357,204 +357,219 @@ public class WbsTreeContractController extends BladeController {
                 throw new ServiceException("未获取到对应的表的html信息");
             }
 
+            InputStream inputStreamByUrl = FileUtils.getInputStreamByUrl(htmlUrl);
+            String htmlString = IoUtil.readToString(inputStreamByUrl);
             //将html转换为excel]
+            org.apache.poi.ss.usermodel.Workbook workbook1 = HtmlTableToExcelConverter.convertHtmlTableToExcel(htmlString);
 
-            htmlUrl = CommonUtil.replaceOssUrl(htmlUrl);
-            InputStream htmlInputStream = FileUtils.getInputStreamByUrl(htmlUrl);
-            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-            LoadOptions lo = new LoadOptions(LoadFormat.HTML);
-            workbook = new Workbook(htmlInputStream,lo);
 
-            workbook.save(byteArrayOutputStream, SaveFormat.XLSX);
+            //返回响应
+            try (ServletOutputStream outputStream = response.getOutputStream();
+                 ByteArrayOutputStream byteArrayOutputStreamResult = new ByteArrayOutputStream()) {
+                //设置响应头
+                response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
+                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+                workbook1.write(byteArrayOutputStreamResult);
+                byte[] excelBytesResult = byteArrayOutputStreamResult.toByteArray();
+                outputStream.write(excelBytesResult);
+            } catch (IOException e) {
+                logger.error("下载excel时出现异常:" + e.getMessage(), e);
+                e.printStackTrace();
+            }
 
-            //将转换的spireExcel存储到流中
-            byte[] excelBytes = byteArrayOutputStream.toByteArray();
+//            htmlUrl = CommonUtil.replaceOssUrl(htmlUrl);
+//            InputStream htmlInputStream = FileUtils.getInputStreamByUrl(htmlUrl);
+//            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+//            LoadOptions lo = new LoadOptions(LoadFormat.HTML);
+//            workbook = new Workbook(htmlInputStream,lo);
+//
+//            workbook.save(byteArrayOutputStream, SaveFormat.XLSX);
+//
+//            //将转换的spireExcel存储到流中
+//            byte[] excelBytes = byteArrayOutputStream.toByteArray();
 
-            //把spireExcel转为poiExcel
-            try (InputStream inputStream = new ByteArrayInputStream(excelBytes)) {
-                org.apache.poi.ss.usermodel.Workbook poiWorkbook = new XSSFWorkbook(inputStream);
-                Sheet poiSheet = poiWorkbook.getSheetAt(0); //获取第一个工作表
-                //存储需要修改的单元格和相关信息的列表
-                List<CellModificationInfo> cellsToModify = new ArrayList<>();
-                //解析html 获取合并单元格
-                InputStream inputStreamByUrl = FileUtils.getInputStreamByUrl(htmlUrl);
-                String htmlString = IoUtil.readToString(inputStreamByUrl);
-                Document doc = Jsoup.parse(htmlString);
-                Element table = doc.select("table").first();
-                Elements rows = table.select("tr");
-                //最大列数
-                int maxColumns = calculateMaxColumns(rows);
-                List<CellRangeAddress> mergedRegions = new ArrayList<>();
-                // 保存需要保留的单元格内容和样式
-                Map<CellRangeAddress, String> data = new HashMap<>();
-                Map<Integer,Integer> rowHeight = new HashMap<>();
-                // 记录每个单元格的位置和合并信息
-                for (int i=0;i<rows.size();i++) {
-                    Element row1 = rows.get(i);
-                    Elements cells = row1.select("td, th");
-                    int colVal = 0;
-                    int index = 0;
-                    for (int j=0;j<cells.size();j++) {
-                        Element cell = cells.get(j);
-                        // 获取html每行的高度
-                        String style = cell.attr("style");
-                        Pattern pattern = Pattern.compile("height:\\s*(\\d+)px");
-                        Matcher matcher = pattern.matcher(style);
-                        if (matcher.find()) {
-                            int height = Integer.parseInt(matcher.group(1));
-                            if(height>0){
-                                rowHeight.put(i, height);  // 记录行高
-                            }
-                        }
 
-                        int rowspan = cell.hasAttr("rowspan") ? Integer.parseInt(cell.attr("rowspan")) : 1; //
-                        int colspan = cell.hasAttr("colspan") ? Integer.parseInt(cell.attr("colspan")) : 1;
-                        int x1 =-1;
-                        int x2 =-1;
-                        int y1 =-1;
-                        int y2 =-1;
-
-                        if(cell.hasAttr("x1") && cell.hasAttr("y1")){ //直接获取
-                            x1 = Integer.parseInt(cell.attr("x1")) - 1; //: -1;//起始列
-                            x2 = Integer.parseInt(cell.attr("x2")) - 1; //: -1;//结束列
-                            y1 = Integer.parseInt(cell.attr("y1")) - 1; //: -1;//起始行
-                            y2 = Integer.parseInt(cell.attr("y2")) - 1; //: -1;//结束行
-                            colVal=x2+1;
-                        }else{
-                            Elements ids = cell.getElementsByAttribute("id");
-                            if(ids.size()==1){
-                                Element cell2 = ids.get(0);
-                                x1 = Integer.parseInt(cell2.attr("x1")) - 1; //: -1;//起始列
-                                x2 = Integer.parseInt(cell2.attr("x2")) - 1; //: -1;//结束列
-                                y1 = Integer.parseInt(cell2.attr("y1")) - 1; //: -1;//起始行
-                                y2 = Integer.parseInt(cell2.attr("y2")) - 1; //: -1;//结束行
-                                colVal=x2+1;
-                            }else{
-                                System.out.println(cell.text());
-
-                                if(i <=80) {
-                                    if (j == 0) {
-                                        x1 = 0;
-                                        x2 = colspan - 1;
-                                        y1 = i;
-                                        y2 = i + rowspan - 1;
-                                        colVal = x2 + 1;
-                                    } else {
-                                        if(colspan>=2){
-                                            x1 = colVal;
-                                            x2 = colVal + colspan - 1;
-                                        }else{
-                                            x1 = colVal;
-                                            x2 = colVal ;
-                                        }
 
-                                        y1 = i;
-                                        y2 = i + rowspan - 1;
-                                        colVal = x2 + 1;
-                                    }
-                                }
-                            }
-                        }
 
-                        // 记录合并区域
-                        if (x1 != -1 && x2 != -1 && y1 != -1 && y2 != -1) {
-                            if(rowspan >=2 || colspan >=2){
-                                CellRangeAddress region = new CellRangeAddress(y1, y2, x1, x2);
-                                mergedRegions.add(region);
-                                data.put(region, cell.text());
-                            }
-                        }
-                        index ++;
-                    }
-                }
-                // 按顺序合并单元格(从上到下、从左到右)
-                for (int i = poiSheet.getNumMergedRegions() - 1; i >= 0; i--) {
-                    poiSheet.removeMergedRegion(i);
-                }
-                for (CellRangeAddress region : mergedRegions) {
-                    poiSheet.addMergedRegion(region);
-                }
-
-                for (Row row : poiSheet) {
-                    //判断当前行是否是最大列 如果不是 就补充到最大列
-                    short lastCellNum = row.getLastCellNum();
 
-                    int rowIndex = row.getRowNum();
-                    if (rowHeight.containsKey(rowIndex)) {
-                        // 将像素转换为POI的行高单位(1px ≈ 0.75 points)
-                        row.setHeightInPoints(rowHeight.get(rowIndex) * 0.75f);
-                    }
 
-                    if(maxColumns > lastCellNum){
-                        for (int i = maxColumns - 1; i > lastCellNum; i--) {
-                            row.createCell(i);
-                            Cell cell = row.getCell(i);
-                            //设置新单元格的样式为第一个单元格的样式
-                            cell.setCellStyle(row.getCell(0).getCellStyle());
-                        }
-                    }
-                    for (Cell cell : row) {
-                        int cellType = cell.getCellType();
-                        if (cellType == CellType.STRING.getCode() && ObjectUtil.isNotEmpty(cell.getStringCellValue())) {
-                            //存储需要修改的单元格信息
-                            cellsToModify.add(new CellModificationInfo(cell, cell.getStringCellValue().trim()));
-                        }
-                        //获取单元格所属的合并单元格区域
-                        CellRangeAddress mergedRegion = findMergedRegion(poiSheet, cell.getRowIndex(), cell.getColumnIndex());
-                        if (mergedRegion != null) {
-                            //合并单元格的数据 为之前记录的数据
-                            cell.setCellValue(data.get(mergedRegion));
-
-                            //存储需要修改的合并单元格信息
-                            cellsToModify.add(new CellModificationInfo(cell, mergedRegion));
-                        }
-                    }
-                }
 
-                //遍历结束后,实际修改单元格样式和内容
-                for (CellModificationInfo info : cellsToModify) {
-                    Cell cell = info.getCell();
-                    //只更新非合并单元格的数据
-                    if (info.getMergedRegion() == null && info.hasStringContent()) {
-                        cell.setCellValue(info.getStringContent());
-                    }
-                    //复制单元格样式
-                    CellStyle cellStyle = poiWorkbook.createCellStyle();
-                    CellStyle sourceCellStyle = cell.getCellStyle(); //获取原单元格的样式
-                    cellStyle.cloneStyleFrom(sourceCellStyle); //复制样式
-                    setBlackBorder(cellStyle); //设置边框
-                    cell.setCellStyle(cellStyle);
-
-//                    if (info.hasMergedRegion()) { //修改合并单元格边框线
-//                        CellRangeAddress mergedRegion = info.getMergedRegion();
-//                        RegionUtil.setBorderTop(BorderStyle.THIN, mergedRegion, poiSheet);
-//                        RegionUtil.setTopBorderColor(IndexedColors.BLACK.getIndex(), mergedRegion, poiSheet);
-//                        RegionUtil.setBorderBottom(BorderStyle.THIN, mergedRegion, poiSheet);
-//                        RegionUtil.setBottomBorderColor(IndexedColors.BLACK.getIndex(), mergedRegion, poiSheet);
-//                        RegionUtil.setBorderLeft(BorderStyle.THIN, mergedRegion, poiSheet);
-//                        RegionUtil.setLeftBorderColor(IndexedColors.BLACK.getIndex(), mergedRegion, poiSheet);
-//                        RegionUtil.setBorderRight(BorderStyle.THIN, mergedRegion, poiSheet);
-//                        RegionUtil.setRightBorderColor(IndexedColors.BLACK.getIndex(), mergedRegion, poiSheet);
+            //把spireExcel转为poiExcel
+//            try (InputStream inputStream = new ByteArrayInputStream(excelBytes)) {
+//                org.apache.poi.ss.usermodel.Workbook poiWorkbook = new XSSFWorkbook(inputStream);
+//                Sheet poiSheet = poiWorkbook.getSheetAt(0); //获取第一个工作表
+//                //存储需要修改的单元格和相关信息的列表
+//                List<CellModificationInfo> cellsToModify = new ArrayList<>();
+//                //解析html 获取合并单元格
+//                InputStream inputStreamByUrl = FileUtils.getInputStreamByUrl(htmlUrl);
+//                String htmlString = IoUtil.readToString(inputStreamByUrl);
+//                Document doc = Jsoup.parse(htmlString);
+//                Element table = doc.select("table").first();
+//                Elements rows = table.select("tr");
+//                //最大列数
+//                int maxColumns = calculateMaxColumns(rows);
+//                List<CellRangeAddress> mergedRegions = new ArrayList<>();
+//                // 保存需要保留的单元格内容和样式
+//                Map<CellRangeAddress, String> data = new HashMap<>();
+//                Map<Integer,Integer> rowHeight = new HashMap<>();
+//
+//
+//
+//                // 记录每个单元格的位置和合并信息
+//                for (int i=0;i<rows.size();i++) {
+//                    Element row1 = rows.get(i);
+//                    Elements cells = row1.select("td, th");
+//                    int colVal = 0;
+//                    int index = 0;
+//                    for (int j=0;j<cells.size();j++) {
+//                        Element cell = cells.get(j);
+//                        // 获取html每行的高度
+//                        String style = cell.attr("style");
+//                        Pattern pattern = Pattern.compile("height:\\s*(\\d+)px");
+//                        Matcher matcher = pattern.matcher(style);
+//                        if (matcher.find()) {
+//                            int height = Integer.parseInt(matcher.group(1));
+//                            if(height>0){
+//                                rowHeight.put(i, height);  // 记录行高
+//                            }
+//                        }
+//
+//                        int rowspan = cell.hasAttr("rowspan") ? Integer.parseInt(cell.attr("rowspan")) : 1; //
+//                        int colspan = cell.hasAttr("colspan") ? Integer.parseInt(cell.attr("colspan")) : 1;
+//                        int x1 =-1;
+//                        int x2 =-1;
+//                        int y1 =-1;
+//                        int y2 =-1;
+//
+//                        if(cell.hasAttr("x1") && cell.hasAttr("y1")){ //直接获取
+//                            x1 = Integer.parseInt(cell.attr("x1")) - 1; //: -1;//起始列
+//                            x2 = Integer.parseInt(cell.attr("x2")) - 1; //: -1;//结束列
+//                            y1 = Integer.parseInt(cell.attr("y1")) - 1; //: -1;//起始行
+//                            y2 = Integer.parseInt(cell.attr("y2")) - 1; //: -1;//结束行
+//                            colVal=x2+1;
+//                        }else{
+//                            Elements ids = cell.getElementsByAttribute("id");
+//                            if(ids.size()==1){
+//                                Element cell2 = ids.get(0);
+//                                x1 = Integer.parseInt(cell2.attr("x1")) - 1; //: -1;//起始列
+//                                x2 = Integer.parseInt(cell2.attr("x2")) - 1; //: -1;//结束列
+//                                y1 = Integer.parseInt(cell2.attr("y1")) - 1; //: -1;//起始行
+//                                y2 = Integer.parseInt(cell2.attr("y2")) - 1; //: -1;//结束行
+//                                colVal=x2+1;
+//                            }else{
+//                                System.out.println(cell.text());
+//
+//                                if(i <=80) {
+//                                    if (j == 0) {
+//                                        x1 = 0;
+//                                        x2 = colspan - 1;
+//                                        y1 = i;
+//                                        y2 = i + rowspan - 1;
+//                                        colVal = x2 + 1;
+//                                    } else {
+//                                        if(colspan>=2){
+//                                            x1 = colVal;
+//                                            x2 = colVal + colspan - 1;
+//                                        }else{
+//                                            x1 = colVal;
+//                                            x2 = colVal ;
+//                                        }
+//
+//                                        y1 = i;
+//                                        y2 = i + rowspan - 1;
+//                                        colVal = x2 + 1;
+//                                    }
+//                                }
+//                            }
+//                        }
+//
+//                        // 记录合并区域
+//                        if (x1 != -1 && x2 != -1 && y1 != -1 && y2 != -1) {
+//                            if(rowspan >=2 || colspan >=2){
+//                                CellRangeAddress region = new CellRangeAddress(y1, y2, x1, x2);
+//                                mergedRegions.add(region);
+//                                data.put(region, cell.text());
+//                            }
+//                        }
+//                        index ++;
 //                    }
-                }
-
-                //返回响应
-                try (ServletOutputStream outputStream = response.getOutputStream();
-                     ByteArrayOutputStream byteArrayOutputStreamResult = new ByteArrayOutputStream()) {
-                    //设置响应头
-                    response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
-                    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
-                    poiWorkbook.write(byteArrayOutputStreamResult);
-                    byte[] excelBytesResult = byteArrayOutputStreamResult.toByteArray();
-                    outputStream.write(excelBytesResult);
-                } catch (IOException e) {
-                    logger.error("下载excel时出现异常:" + e.getMessage(), e);
-                    e.printStackTrace();
-                }
-            } catch (IOException e) {
-                logger.error("下载excel时出现异常:" + e.getMessage(), e);
-                e.printStackTrace();
-            }
+//                }
+//                // 按顺序合并单元格(从上到下、从左到右)
+//                for (int i = poiSheet.getNumMergedRegions() - 1; i >= 0; i--) {
+//                    poiSheet.removeMergedRegion(i);
+//                }
+//                for (CellRangeAddress region : mergedRegions) {
+//                    poiSheet.addMergedRegion(region);
+//                }
+//
+//                for (Row row : poiSheet) {
+//                    //判断当前行是否是最大列 如果不是 就补充到最大列
+//                    short lastCellNum = row.getLastCellNum();
+//
+//                    int rowIndex = row.getRowNum();
+//                    if (rowHeight.containsKey(rowIndex)) {
+//                        // 将像素转换为POI的行高单位(1px ≈ 0.75 points)
+//                        row.setHeightInPoints(rowHeight.get(rowIndex) * 0.75f);
+//                    }
+//
+//                    if(maxColumns > lastCellNum){
+//                        for (int i = maxColumns - 1; i > lastCellNum; i--) {
+//                            row.createCell(i);
+//                            Cell cell = row.getCell(i);
+//                            //设置新单元格的样式为第一个单元格的样式
+//                            cell.setCellStyle(row.getCell(0).getCellStyle());
+//                        }
+//                    }
+//                    for (Cell cell : row) {
+//                        int cellType = cell.getCellType();
+//                        if (cellType == CellType.STRING.getCode() && ObjectUtil.isNotEmpty(cell.getStringCellValue())) {
+//                            //存储需要修改的单元格信息
+//                            cellsToModify.add(new CellModificationInfo(cell, cell.getStringCellValue().trim()));
+//                        }
+//                        //获取单元格所属的合并单元格区域
+//                        CellRangeAddress mergedRegion = findMergedRegion(poiSheet, cell.getRowIndex(), cell.getColumnIndex());
+//                        if (mergedRegion != null) {
+//                            //合并单元格的数据 为之前记录的数据
+//                            cell.setCellValue(data.get(mergedRegion));
+//
+//                            //存储需要修改的合并单元格信息
+//                            cellsToModify.add(new CellModificationInfo(cell, mergedRegion));
+//                        }
+//                    }
+//                }
+//
+//                //遍历结束后,实际修改单元格样式和内容
+//                for (CellModificationInfo info : cellsToModify) {
+//                    Cell cell = info.getCell();
+//                    //只更新非合并单元格的数据
+//                    if (info.getMergedRegion() == null && info.hasStringContent()) {
+//                        cell.setCellValue(info.getStringContent());
+//                    }
+//                    //复制单元格样式
+//                    CellStyle cellStyle = poiWorkbook.createCellStyle();
+//                    CellStyle sourceCellStyle = cell.getCellStyle(); //获取原单元格的样式
+//                    cellStyle.cloneStyleFrom(sourceCellStyle); //复制样式
+//                    setBlackBorder(cellStyle); //设置边框
+//                    cell.setCellStyle(cellStyle);
+//
+////                    if (info.hasMergedRegion()) { //修改合并单元格边框线
+////                        CellRangeAddress mergedRegion = info.getMergedRegion();
+////                        RegionUtil.setBorderTop(BorderStyle.THIN, mergedRegion, poiSheet);
+////                        RegionUtil.setTopBorderColor(IndexedColors.BLACK.getIndex(), mergedRegion, poiSheet);
+////                        RegionUtil.setBorderBottom(BorderStyle.THIN, mergedRegion, poiSheet);
+////                        RegionUtil.setBottomBorderColor(IndexedColors.BLACK.getIndex(), mergedRegion, poiSheet);
+////                        RegionUtil.setBorderLeft(BorderStyle.THIN, mergedRegion, poiSheet);
+////                        RegionUtil.setLeftBorderColor(IndexedColors.BLACK.getIndex(), mergedRegion, poiSheet);
+////                        RegionUtil.setBorderRight(BorderStyle.THIN, mergedRegion, poiSheet);
+////                        RegionUtil.setRightBorderColor(IndexedColors.BLACK.getIndex(), mergedRegion, poiSheet);
+////                    }
+//                }
+//
+//
+//            } catch (IOException e) {
+//                logger.error("下载excel时出现异常:" + e.getMessage(), e);
+//                e.printStackTrace();
+//            }
         } catch (Exception e) {
             logger.error("下载excel时出现异常:" + e.getMessage(), e);
             e.printStackTrace();