From 09f75e1b4ee21241940ad26b91462ea15c5e4f00 Mon Sep 17 00:00:00 2001
From: liusheng <337615773@qq.com>
Date: 星期五, 03 七月 2026 09:25:29 +0800
Subject: [PATCH] 1.短信功能提交 2.优化随访详情页 3.导出两个sheet

---
 ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java |  275 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 246 insertions(+), 29 deletions(-)

diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
index ac4dd92..f17faa3 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
@@ -38,14 +38,9 @@
 import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.ss.util.CellRangeAddressList;
 import org.apache.poi.util.IOUtils;
+import org.apache.poi.xddf.usermodel.chart.*;
 import org.apache.poi.xssf.streaming.SXSSFWorkbook;
-import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
-import org.apache.poi.xssf.usermodel.XSSFDataValidation;
-import org.apache.poi.xssf.usermodel.XSSFDrawing;
-import org.apache.poi.xssf.usermodel.XSSFPicture;
-import org.apache.poi.xssf.usermodel.XSSFShape;
-import org.apache.poi.xssf.usermodel.XSSFSheet;
-import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.apache.poi.xssf.usermodel.*;
 import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -444,6 +439,102 @@
         response.setCharacterEncoding("utf-8");
         this.init(list, sheetName, title, Type.EXPORT);
         exportExcel(response);
+    }
+
+    /**
+     * 瀵煎嚭鍖呭惈涓や釜Sheet鐨凟xcel
+     * Sheet1: 鍩轰簬褰撳墠娉涘瀷绫诲瀷T鐨勬敞瑙e鍑�
+     * Sheet2: 鍩轰簬浼犲叆鐨刢lazz2绫诲瀷鐨勬敞瑙e鍑�
+     *
+     * @param response   杩斿洖鏁版嵁
+     * @param list1      绗竴涓猄heet鐨勬暟鎹泦鍚�
+     * @param sheetName1 绗竴涓猄heet鐨勫悕绉�
+     * @param list2      绗簩涓猄heet鐨勬暟鎹泦鍚�
+     * @param sheetName2 绗簩涓猄heet鐨勫悕绉�
+     * @param clazz2     绗簩涓猄heet鏁版嵁瀵硅薄鐨凜lass
+     */
+    public void exportExcelTwoSheet(HttpServletResponse response,
+                                    List<T> list1, String sheetName1,
+                                    List<?> list2, String sheetName2, Class<?> clazz2) {
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setCharacterEncoding("utf-8");
+        // 鍒濆鍖栧苟鍐欏叆Sheet1
+        this.init(list1, sheetName1, StringUtils.EMPTY, Type.EXPORT);
+        writeSheet();
+        // 杩藉姞鍐欏叆Sheet2
+        writeAdditionalSheet(list2, sheetName2, clazz2);
+        // 杈撳嚭鍒板搷搴旀祦
+        try {
+            wb.write(response.getOutputStream());
+        } catch (Exception e) {
+            log.error("瀵煎嚭Excel寮傚父{}", e.getMessage());
+        } finally {
+            IOUtils.closeQuietly(wb);
+        }
+    }
+
+    /**
+     * 鍚戝綋鍓嶅伐浣滅翱杩藉姞鍐欏叆涓�涓猄heet
+     *
+     * @param dataList  鏁版嵁闆嗗悎
+     * @param sheetName Sheet鍚嶇О
+     * @param dataClazz 鏁版嵁瀵硅薄鐨凜lass
+     */
+    @SuppressWarnings("unchecked")
+    public void writeAdditionalSheet(List<?> dataList, String sheetName, Class<?> dataClazz) {
+        if (dataList == null) {
+            dataList = new ArrayList<>();
+        }
+        // 淇濆瓨鍘熷鐘舵��
+        Class<T> originClazz = this.clazz;
+        List<Object[]> originFields = this.fields;
+        List<T> originList = this.list;
+        String originSheetName = this.sheetName;
+        int originRownum = this.rownum;
+        List<Field> originSubFields = this.subFields;
+        Method originSubMethod = this.subMethod;
+        int originSubMergedFirstRowNum = this.subMergedFirstRowNum;
+        int originSubMergedLastRowNum = this.subMergedLastRowNum;
+        Map<Integer, Double> originStatistics = this.statistics;
+        short originMaxHeight = this.maxHeight;
+
+        // 璁剧疆鏂扮被鍨�
+        this.clazz = (Class<T>) dataClazz;
+        this.list = (List<T>) dataList;
+        this.sheetName = sheetName;
+        this.rownum = 0;
+        this.subFields = null;
+        this.subMethod = null;
+        this.subMergedFirstRowNum = 1;
+        this.subMergedLastRowNum = 0;
+        this.statistics = new HashMap<Integer, Double>();
+
+        // 鍒涘缓鏂癝heet
+        this.sheet = wb.createSheet();
+        wb.setSheetName(wb.getNumberOfSheets() - 1, sheetName);
+
+        // 閲嶆柊鍒涘缓瀛楁
+        createExcelField();
+
+        // 琛ュ厖鏂板瓧娈电殑鏍峰紡
+        this.styles.putAll(annotationHeaderStyles(wb, this.styles));
+        this.styles.putAll(annotationDataStyles(wb));
+
+        // 鍐欏叆鏁版嵁
+        writeSheet();
+
+        // 鎭㈠鍘熷鐘舵��
+        this.clazz = originClazz;
+        this.fields = originFields;
+        this.list = originList;
+        this.sheetName = originSheetName;
+        this.rownum = originRownum;
+        this.subFields = originSubFields;
+        this.subMethod = originSubMethod;
+        this.subMergedFirstRowNum = originSubMergedFirstRowNum;
+        this.subMergedLastRowNum = originSubMergedLastRowNum;
+        this.statistics = originStatistics;
+        this.maxHeight = originMaxHeight;
     }
 
     /**
@@ -1449,19 +1540,6 @@
     }
 
     /**
-     * 鍒ゆ柇鏄惁涓轰綆鐗堟湰娴忚鍣�
-     *
-     * @param request 娴忚鍣ㄨ姹傚ご
-     * @return
-     */
-    public static boolean isLowVersionBrowser(HttpServletRequest request) {
-        String userAgent = request.getHeader("User-Agent");
-        return userAgent.contains("msie")
-                || userAgent.contains("Trident")
-                || userAgent.contains("Edge");
-    }
-
-    /**
      * 璁剧疆瀵煎嚭Excel鏂囦欢鍚嶇О
      * @param response 杩斿洖鏁版嵁
      * @param request 娴忚鍣ㄨ姹傚ご
@@ -1472,18 +1550,157 @@
             //浼氭竻绌哄搷搴旂紦鍐插尯鐨勫唴瀹癸紝纭繚鍚庣画杈撳嚭涓嶄細鍙楀埌涔嬪墠鏁版嵁鐨勫奖鍝�
             response.reset();
             // 瀹氫箟娴忚鍣ㄥ搷搴旇〃澶达紝椤哄甫瀹氫箟涓嬭浇鍚�
-            if (isLowVersionBrowser(request)) {
-                fileName = URLEncoder.encode(fileName, "UTF8") + ".xlsx";
-                response.setHeader("Content-Disposition",
-                        "attachment;filename=" + fileName);
-            } else {
-                response.setHeader("Content-Disposition",
-                        "attachment;filename=" + new String((fileName + ".xlsx").getBytes("gb2312"), "ISO8859-1"));
-            }
+            fileName = fileName + ".xlsx";
+            // 瀵规枃浠跺悕杩涜URL缂栫爜锛屾敮鎸佷腑鏂囨枃浠跺悕
+            String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
+            // 浣跨敤 filename* 鍙傛暟鏀寔 UTF-8 缂栫爜锛圧FC 5987锛夛紝鍚屾椂淇濈暀 filename 鍙傛暟鍏煎鏃ф祻瑙堝櫒
+            response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName +
+                    "\"; filename*=utf-8''" + encodedFileName);
             // 瀹氫箟涓嬭浇鐨勭被鍨嬶紝鏍囨槑鏄痚xcel鏂囦欢
-            response.setContentType("application/vnd.ms-excel");
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("utf-8");
         }catch (Exception e){
             log.error("{}璁剧疆瀵煎嚭Excel鏂囦欢鍚嶇О寮傚父,{}", fileName, e.getMessage());
         }
     }
+
+//    public static void main(String[] args) {
+//        // 1. 鍒涘缓宸ヤ綔绨�
+//        try (XSSFWorkbook workbook = new XSSFWorkbook()) {
+//            // 2. 鐢熸垚鏌辩姸鍥� Sheet
+//            createBarChartSheet(workbook);
+//            // 3. 鐢熸垚鎶樼嚎鍥� Sheet
+//            createLineChartSheet(workbook);
+//            // 4. 鐢熸垚楗肩姸鍥� Sheet
+//            createPieChartSheet(workbook);
+//
+//            // 5. 瀵煎嚭鍒版湰鍦版枃浠�
+//            try (FileOutputStream fos = new FileOutputStream("D:/鍥捐〃瀵煎嚭.xlsx")) {
+//                workbook.write(fos);
+//                System.out.println("Excel 鍥捐〃瀵煎嚭鎴愬姛锛佽矾寰勶細D:/鍥捐〃瀵煎嚭.xlsx");
+//            }
+//        } catch (IOException e) {
+//            e.printStackTrace();
+//        }
+//    }
+
+    // ==================== 1. 鍒涘缓鏌辩姸鍥� Sheet ====================
+    private static void createBarChartSheet(XSSFWorkbook workbook) {
+        XSSFSheet sheet = workbook.createSheet("鏌辩姸鍥�");
+        writeChartData(sheet);
+
+        XSSFDrawing drawing = sheet.createDrawingPatriarch();
+        XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 3, 1, 12, 15);
+        XSSFChart chart = drawing.createChart(anchor);
+        chart.setTitleText("浜у搧閿�閲�-鏌辩姸鍥�");
+        chart.setTitleOverlay(false);
+
+        // 鍧愭爣杞�
+        XDDFCategoryAxis xAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
+        XDDFValueAxis yAxis = chart.createValueAxis(AxisPosition.LEFT);
+        yAxis.setCrosses(AxisCrosses.AUTO_ZERO);
+
+        // 鏁版嵁鑼冨洿锛氬垎绫籄2:A6锛屾暟鍊糂2:B6
+        XDDFDataSource<String> cat = XDDFDataSourcesFactory.fromStringCellRange(sheet,
+                new CellRangeAddress(1, 5, 0, 0));
+        XDDFNumericalDataSource<Double> val = XDDFDataSourcesFactory.fromNumericCellRange(sheet,
+                new CellRangeAddress(1, 5, 1, 1));
+
+        // 鏌辩姸鍥炬暟鎹�
+        XDDFBarChartData barChart = (XDDFBarChartData) chart.createData(ChartTypes.BAR, xAxis, yAxis);
+        XDDFBarChartData.Series series = (XDDFBarChartData.Series) barChart.addSeries(cat, val);
+        series.setTitle("閿�閲�", null);
+        barChart.setBarDirection(BarDirection.COL);
+
+        // 缁樺埗锛堝叧閿細缂哄け灏变笉鏄剧ず鍥捐〃锛�
+        chart.plot(barChart);
+    }
+
+    // ==================== 2. 鍒涘缓鎶樼嚎鍥� Sheet ====================
+    private static void createLineChartSheet(XSSFWorkbook workbook) {
+        XSSFSheet sheet = workbook.createSheet("鎶樼嚎鍥�");
+        // 鍐欏叆琛ㄥご + 鏁版嵁
+        writeChartData(sheet);
+
+        // 缁樺浘鍖哄煙
+        XSSFDrawing drawing = sheet.createDrawingPatriarch();
+        XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, 5, 10, 20);
+        XSSFChart chart = drawing.createChart(anchor);
+
+        // 鍥捐〃鏍囬
+        chart.setTitleText("鏈堝害閿�鍞鎶樼嚎鍥�");
+        chart.setTitleOverlay(false);
+
+        // 鍥句緥
+        XDDFChartLegend legend = chart.getOrAddLegend();
+        legend.setPosition(LegendPosition.TOP_RIGHT);
+
+        // X杞淬�乊杞�
+        XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
+        bottomAxis.setTitle("鏈堜唤");
+        XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
+        leftAxis.setTitle("閿�鍞锛堜竾鍏冿級");
+
+        // 鏁版嵁鑼冨洿
+        XDDFDataSource<String> months = XDDFDataSourcesFactory.fromStringCellRange(sheet, new CellRangeAddress(0, 5, 0, 0));
+        XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromNumericCellRange(sheet, new CellRangeAddress(0, 5, 1, 1));
+
+        // 鍒涘缓鎶樼嚎鍥�
+        XDDFLineChartData lineData = (XDDFLineChartData) chart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
+        XDDFLineChartData.Series series = (XDDFLineChartData.Series) lineData.addSeries(months, values);
+        series.setTitle("閿�鍞", null);
+        series.setMarkerStyle(MarkerStyle.CIRCLE); // 鏄剧ず鍦嗙偣
+
+        chart.plot(lineData);
+    }
+
+    // ========== 鏂板锛氶ゼ鐘跺浘 ==========
+    private static void createPieChartSheet(XSSFWorkbook workbook) {
+        XSSFSheet sheet = workbook.createSheet("楗肩姸鍥�");
+        writeChartData(sheet);
+
+        XSSFDrawing drawing = sheet.createDrawingPatriarch();
+        XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 3, 1, 12, 15);
+        XSSFChart chart = drawing.createChart(anchor);
+        chart.setTitleText("浜у搧鍗犳瘮-楗肩姸鍥�");
+        chart.setTitleOverlay(false);
+
+        // 楗煎浘涓嶉渶瑕乆Y杞�
+        XDDFDataSource<String> cat = XDDFDataSourcesFactory.fromStringCellRange(sheet,
+                new CellRangeAddress(1, 5, 0, 0));
+        XDDFNumericalDataSource<Double> val = XDDFDataSourcesFactory.fromNumericCellRange(sheet,
+                new CellRangeAddress(1, 5, 1, 1));
+
+        // 楗煎浘鏁版嵁
+        XDDFPieChartData pieChart = (XDDFPieChartData) chart.createData(ChartTypes.PIE, null, null);
+        pieChart.addSeries(cat, val);
+        chart.plot(pieChart);
+    }
+
+    // ==================== 鍏叡鏂规硶锛氬啓鍏ュ浘琛ㄦ暟鎹� ====================
+    private static void writeChartData(XSSFSheet sheet) {
+        Object[][] data = {
+                {"鍒嗙被", "鏁板��"},
+                {"浜у搧A", 35},
+                {"浜у搧B", 28},
+                {"浜у搧C", 42},
+                {"浜у搧D", 19},
+                {"浜у搧E", 26}
+        };
+        int rowIdx = 0;
+        for (Object[] rows : data) {
+            Row row = sheet.createRow(rowIdx++);
+            int cellIdx = 0;
+            for (Object val : rows) {
+                if (val instanceof String) {
+                    row.createCell(cellIdx++).setCellValue((String) val);
+                } else if (val instanceof Number) {
+                    row.createCell(cellIdx++).setCellValue(((Number) val).doubleValue());
+                }
+            }
+        }
+        sheet.autoSizeColumn(0);
+        sheet.autoSizeColumn(1);
+    }
+
 }

--
Gitblit v1.9.3