From a1eabf01b3dbec288897d4d1cc400cfdad076d1f Mon Sep 17 00:00:00 2001
From: 无关风月 <443237572@qq.com>
Date: 星期四, 03 七月 2025 13:57:35 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 ruoyi-system/src/main/java/com/ruoyi/system/vo/system/ProjectDeptDetailsChildVO.java   |   16 +
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/ReportController.java           |  138 +++++++++++++
 ruoyi-system/src/main/java/com/ruoyi/system/vo/system/TaskSummaryVO.java               |   39 +++
 ruoyi-admin/pom.xml                                                                    |   20 +-
 ruoyi-system/pom.xml                                                                   |   12 
 ruoyi-system/src/main/java/com/ruoyi/system/utils/ExcelPoiUtils.java                   |  333 +++++++++++++++++++++++++++++++++
 ruoyi-system/src/main/java/com/ruoyi/system/vo/system/ProjectDeptDetailsVO.java        |   10 
 ruoyi-system/src/main/java/com/ruoyi/system/vo/system/LocationTypeListByProjectVO.java |    3 
 8 files changed, 549 insertions(+), 22 deletions(-)

diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index a3efa13..3097d11 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -148,16 +148,16 @@
 
         <!-- excel工具 -->
         <!-- Apache POI核心依赖 -->
-        <dependency>
-            <groupId>org.apache.poi</groupId>
-            <artifactId>poi</artifactId>
-            <version>3.17</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.poi</groupId>
-            <artifactId>poi-ooxml</artifactId>
-            <version>3.17</version>
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>org.apache.poi</groupId>-->
+<!--            <artifactId>poi</artifactId>-->
+<!--            <version>3.17</version>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>org.apache.poi</groupId>-->
+<!--            <artifactId>poi-ooxml</artifactId>-->
+<!--            <version>3.17</version>-->
+<!--        </dependency>-->
 
         <dependency>
             <groupId>net.coobird</groupId>
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/ReportController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/ReportController.java
index 9b5aceb..eb98463 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/ReportController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/ReportController.java
@@ -16,6 +16,7 @@
 import com.ruoyi.system.model.*;
 import com.ruoyi.system.query.*;
 import com.ruoyi.system.service.*;
+import com.ruoyi.system.utils.ExcelPoiUtils;
 import com.ruoyi.system.vo.system.*;
 import com.ruoyi.web.util.OssUploadUtil;
 import io.swagger.annotations.Api;
@@ -479,7 +480,6 @@
                     locationTypeListByProjectVOS1.add(temp);
                 }
                 projectDeptDetailsChildVO.setLocationTypeList(locationTypeListByProjectVOS1);
-                projectDeptDetailsChildVO.setCleanerCount(0);
 
             }
 
@@ -631,7 +631,6 @@
 
     @ApiOperation(value = "巡检人员报表")
     @PostMapping(value = "/patrolInspectorList")
-
     public R<List<PatrolInspectorVO>> patrolInspectorList(@RequestBody PatrolInspectorQuery query) {
         List<SysUser> sysUsers = sysUserService.selectAllList();
         LambdaQueryWrapper<TLeave> tLeaveLambdaQueryWrapper = new LambdaQueryWrapper<>();
@@ -1075,5 +1074,140 @@
             }
         }
     }
+
+
+    @ApiOperation(value = "任务报表导出模板")
+    @PostMapping(value = "/exportExcel")
+    public void exportExcel(@RequestBody TaskDetailsQuery query,HttpServletResponse response) throws Exception{
+        // 动态标头名称字段
+        final String headerName = "locationTypeName";
+        // 动态标头值字段
+        final String headerValue = "locationNum";
+        List<TLocationType> locationTypeList = locationTypeService.list();
+        List<TLocation> locations = locationService.list();
+        LambdaQueryWrapper<TTask> tTaskLambdaQueryWrapper = new LambdaQueryWrapper<>();
+        if (StringUtils.hasLength(query.getStartTime()) && StringUtils.hasLength(query.getEndTime())) {
+            tTaskLambdaQueryWrapper.ge(TTask::getImplementTime, query.getStartTime());
+            tTaskLambdaQueryWrapper.le(TTask::getImplementTime, query.getEndTime());
+        }
+        List<ProjectDeptDetailsVO> res = new ArrayList<>();
+        List<TTask> tasks = taskCleanerService.list(tTaskLambdaQueryWrapper);
+        List<TTaskDetail> taskDetails = taskDetailService.lambdaQuery().orderByDesc(BaseModel::getCreateTime).list();
+        List<String> collect = tasks.stream().map(TTask::getProjectId).distinct().collect(Collectors.toList());
+        List<TProjectDept> projectDepts = projectDeptService.list();
+        List<String> strings = new ArrayList<>();
+
+        // 片区ids 反查项目部
+        List<TProjectDept> list1 = projectDeptService.lambdaQuery().in(TProjectDept::getId, collect).list();
+        for (TProjectDept tProjectDept : list1) {
+            TProjectDept tProjectDept1 = projectDepts.stream().filter(e -> e.getId().equals(tProjectDept.getParentId())).findFirst().orElse(null);
+            if (tProjectDept1 != null && !strings.contains(tProjectDept1.getId())) {
+                ProjectDeptDetailsVO projectDeptDetailsVO = new ProjectDeptDetailsVO();
+                projectDeptDetailsVO.setProjectName(tProjectDept1.getProjectName());
+                List<TProjectDept> collect1 = list1.stream().filter(e -> e.getParentId().equals(tProjectDept1.getId())).collect(Collectors.toList());
+                List<ProjectDeptDetailsChildVO> projectDeptDetailsChildVOS = new ArrayList<>();
+                for (TProjectDept projectDept : collect1) {
+                    ProjectDeptDetailsChildVO projectDeptDetailsChildVO = new ProjectDeptDetailsChildVO();
+                    projectDeptDetailsChildVO.setProjectChildName(projectDept.getProjectName());
+                    projectDeptDetailsChildVO.setProjectId(projectDept.getId());
+                    projectDeptDetailsChildVOS.add(projectDeptDetailsChildVO);
+                }
+                projectDeptDetailsVO.setProjectChild(projectDeptDetailsChildVOS);
+                res.add(projectDeptDetailsVO);
+                strings.add(tProjectDept1.getId());
+            }
+        }
+        List<LocationTypeListByProjectVO> locationTypeListByProjectVOS = new ArrayList<>();
+        for (TLocationType tLocationType : locationTypeList) {
+            LocationTypeListByProjectVO locationTypeListByProjectVO = new LocationTypeListByProjectVO();
+            locationTypeListByProjectVO.setLocationTypeName(tLocationType.getLocationName());
+            locationTypeListByProjectVO.setLocationNum(0);
+            locationTypeListByProjectVO.setId(tLocationType.getId());
+            locationTypeListByProjectVOS.add(locationTypeListByProjectVO);
+        }
+        for (ProjectDeptDetailsVO re : res) {
+            List<ProjectDeptDetailsChildVO> projectChild = re.getProjectChild();
+            for (ProjectDeptDetailsChildVO projectDeptDetailsChildVO : projectChild) {
+                int cleanerCount = cleanerService.lambdaQuery().eq(TCleaner::getProjectId, projectDeptDetailsChildVO.getProjectId()).list().size();
+                projectDeptDetailsChildVO.setCleanerCount(cleanerCount);
+                for (LocationTypeListByProjectVO locationTypeListByProjectVO : locationTypeListByProjectVOS) {
+                    List<TaskSummaryVO> taskSummaryVOS = new ArrayList<>();
+                    List<String> locationIds = locations.stream().filter(e -> e.getLocationType().equals(locationTypeListByProjectVO.getId())).map(TLocation::getId).collect(Collectors.toList());
+                    if (locationIds.isEmpty()) {
+                        locationTypeListByProjectVO.setLocationNum(0);
+                    } else {
+                        List<TTask> taskList = tasks.stream().filter(e -> e.getProjectId().equals(projectDeptDetailsChildVO.getProjectId())
+                                && locationIds.contains(e.getLocationId())).collect(Collectors.toList());
+                        Integer count = taskList.size();
+                        locationTypeListByProjectVO.setLocationNum(count);
+                        int total = taskList.size();
+                        int num1 = 0;
+                        int num2 = 0;
+                        int num3 = 0;
+                        int num4 = 0;
+                        int num5 = 0;
+                        int num6 = 0;
+                        int num7 = 0;
+                        TaskSummaryVO taskSummaryVO = new TaskSummaryVO();
+                        for (TTask tTask : taskList) {
+                            TTaskDetail tTaskDetail = taskDetails.stream().filter(e -> e.getTaskId().equals(tTask.getId()) && e.getClearStatus() != null).findFirst().orElse(null);
+                            if (tTaskDetail!=null){
+                                switch (tTaskDetail.getClearStatus()) {
+                                    case 1:
+                                        num1++;
+                                        break;
+                                    case 2:
+                                        num2++;
+                                        break;
+                                }
+                            }
+                            // 任务状态:1未执行、2超时、3待确认、4待整改、5整改完成、6已完成
+                            switch (tTask.getStatus()) {
+                                case 1:
+                                    if (tTaskDetail == null) {
+                                        num3++;
+                                    }
+                                    break;
+                                case 2:
+                                    num6++;
+                                    break;
+                                case 3:
+                                    break;
+                                case 4:
+                                    num4++;
+                                    break;
+                                case 5:
+                                    num7++;
+                                    break;
+                                case 6:
+                                    num5++;
+                                    break;
+                            }
+                        }
+                        taskSummaryVO.setTotal(total);
+                        taskSummaryVO.setNum1(num1);
+                        taskSummaryVO.setNum2(num2);
+                        taskSummaryVO.setNum3(num3);
+                        taskSummaryVO.setNum4(num4);
+                        taskSummaryVO.setNum5(num5);
+                        taskSummaryVO.setNum6(num6);
+                        taskSummaryVO.setNum7(num7);
+                        taskSummaryVOS.add(taskSummaryVO);
+                    }
+                    projectDeptDetailsChildVO.setTaskSummaryVOS(taskSummaryVOS);
+                }
+                projectDeptDetailsChildVO.setLocationTypeList(locationTypeListByProjectVOS);
+            }
+        }
+        try {
+            String time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy_MM_dd_HH_mm_ss"));
+            ExcelPoiUtils.exportExcel(response,  "任务报表_"+time, "任务报表导出模板",
+                    "sheet", res, headerName, headerValue);
+        } catch (Exception e) {
+            System.out.println("导出错误,"+e.getMessage());
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
 }
 
diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml
index a02c1aa..6f393c1 100644
--- a/ruoyi-system/pom.xml
+++ b/ruoyi-system/pom.xml
@@ -41,17 +41,17 @@
         <dependency>
             <groupId>cn.afterturn</groupId>
             <artifactId>easypoi-base</artifactId>
-            <version>3.0.3</version>
+            <version>4.4.0</version>
         </dependency>
         <dependency>
             <groupId>cn.afterturn</groupId>
             <artifactId>easypoi-web</artifactId>
-            <version>3.0.3</version>
+            <version>4.4.0</version>
         </dependency>
         <dependency>
             <groupId>cn.afterturn</groupId>
             <artifactId>easypoi-annotation</artifactId>
-            <version>3.0.3</version>
+            <version>4.4.0</version>
         </dependency>
 
         <!--mybatis-plus-->
@@ -93,6 +93,12 @@
             <groupId>com.fasterxml.jackson.datatype</groupId>
             <artifactId>jackson-datatype-jsr310</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.83_noneautotype</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/ExcelPoiUtils.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/ExcelPoiUtils.java
new file mode 100644
index 0000000..461e114
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/ExcelPoiUtils.java
@@ -0,0 +1,333 @@
+package com.ruoyi.system.utils;
+ 
+import cn.afterturn.easypoi.excel.ExcelExportUtil;
+import cn.afterturn.easypoi.excel.annotation.Excel;
+import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
+import cn.afterturn.easypoi.excel.entity.ExportParams;
+import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
+import cn.hutool.core.annotation.AnnotationUtil;
+import cn.hutool.core.util.ReflectUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+ 
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Field;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+ 
+/**
+ * @Author: 一杯美式
+ * @version: V1.0
+ * @Date: 2024-04-23 10:38
+ * @Package: com.victory.jfe.pdm.srm.config
+ */
+@Slf4j
+@Service
+public class ExcelPoiUtils {
+    /**
+     * 动态列导出
+     * @param response    响应
+     * @param fileName    文件名
+     * @param title       标题
+     * @param sheetName   sheet名称
+     * @param dataList    导出数据
+     * @param headerName  动态列标题
+     * @param headerValue 动态列值
+     * @param <T>
+     * @throws Exception
+     */
+    public static <T> void exportExcel(HttpServletResponse response, String fileName, String title, String sheetName,
+                                       List<T> dataList, String headerName, String headerValue) throws Exception {
+        if (CollectionUtils.isNotEmpty(dataList)) {
+            T dataInstance = dataList.get(0);
+            List<ExcelExportEntity> entityList = buildExcelExportEntityList(dataInstance, headerName, "");
+            List<Object> objList = convertDataListToObjList(dataList, headerName,headerValue);
+            downloadExcelEntityDynamic(response, entityList, objList, fileName, title, sheetName);
+        }
+    }
+ 
+    public static <T> void exportExcel(HttpServletResponse response, String fileName, String title, String sheetName,
+                                       List<T> dataList, String headerName, String headerValue, String ignoreCol) throws Exception {
+        if (CollectionUtils.isNotEmpty(dataList)) {
+            T dataInstance = dataList.get(0);
+            List<ExcelExportEntity> entityList = buildExcelExportEntityList(dataInstance, headerName, ignoreCol);
+            List<Object> objList = convertDataListToObjList(dataList, headerName,headerValue);
+            downloadExcelEntityDynamic(response, entityList, objList, fileName, title, sheetName);
+        }
+    }
+ 
+    /**
+     * 构建Excel导出实体列表
+     *
+     * @param t          取数据集第一条数据 做实体列表构建
+     * @param headerName 动态列标题
+     * @param <T>        数据类型
+     * @return Excel导出实体列表
+     * @throws IllegalAccessException 如果无法访问字段
+     */
+    private static <T> List<ExcelExportEntity> buildExcelExportEntityList(T t, String headerName, String ignoreCol) throws IllegalAccessException {
+        List<ExcelExportEntity> entityList = new ArrayList<>();
+        Field[] fields = t.getClass().getDeclaredFields();
+        int index = 0;
+ 
+        for (Field field : fields) {
+            field.setAccessible(true);
+            Optional<Excel> excelOpt = Optional.ofNullable(field.getAnnotation(Excel.class));
+            Optional<ExcelCollection> excelCollectionOpt = Optional.ofNullable(field.getAnnotation(ExcelCollection.class));
+ 
+            if (excelOpt.isPresent()) {
+                // 处理固定导出列
+                if (ignoreCol.contains(field.getName())) {
+                    index++;
+                } else {
+                    index = handleFixedExportColumn(entityList, field, excelOpt.get(), index);
+                }
+            } else if (excelCollectionOpt.isPresent() && List.class.isAssignableFrom(field.getType())) {
+                // 处理自定义导出列
+                index = handleCustomExportColumn(t, entityList, field, headerName, index);
+            }
+        }
+ 
+        return entityList;
+    }
+ 
+    /**
+     * 处理固定导出列
+     *
+     * @param entityList 实体列表
+     * @param field      字段
+     * @param excel      Excel注解
+     * @param index      索引
+     * @return 更新后的索引
+     */
+    private static int handleFixedExportColumn(List<ExcelExportEntity> entityList, Field field, Excel excel, int index) {
+        Object name = AnnotationUtil.getAnnotationValue(field, Excel.class, "name");
+        ExcelExportEntity entity = createExcelExportEntity(field, name.toString(), field.getName(), index);
+        entityList.add(entity);
+        return index + 1;
+    }
+ 
+    /**
+     * 处理自定义导出列
+     *
+     * @param t          数据对象
+     * @param entityList 实体列表
+     * @param field      字段
+     * @param headerName 动态列标题
+     * @param index      索引
+     * @param <T>        数据类型
+     * @return 更新后的索引
+     * @throws IllegalAccessException 如果无法访问字段
+     */
+    private static <T> int handleCustomExportColumn(T t, List<ExcelExportEntity> entityList, Field field, String headerName, int index) throws IllegalAccessException {
+        List<?> dynamicColl = (List<?>) field.get(t);
+        for (Object arr : dynamicColl) {
+            Field[] typeFields = arr.getClass().getDeclaredFields();
+            for (Field typeField : typeFields) {
+                typeField.setAccessible(true);
+                Excel excelItem = typeField.getAnnotation(Excel.class);
+                if (excelItem != null && headerName.equals(typeField.getName())) {
+                    Object value = typeField.get(arr);
+                    ExcelExportEntity entity = createExcelExportEntity(typeField, value.toString(), value.toString(), index);
+                    entityList.add(entity);
+                    index++;
+                }
+            }
+        }
+        return index;
+    }
+ 
+    /**
+     * 将数据对象列表转换为对象列表,通过异步处理每个项。
+     *
+     * @param dataList 需要处理的数据对象列表。
+     * @param headerName 用于从对象中提取特定值的标题名称。
+     * @param headerValue 用于从对象中提取特定值的标题值。
+     * @param <T> 数据对象列表中的对象类型。
+     * @return 表示处理过的数据对象的映射列表。
+     */
+    public static <T> List<Object> convertDataListToObjList(List<T> dataList, String headerName, String headerValue) {
+        // 创建一个固定大小的线程池 =处理器数量
+        ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
+        long start = System.currentTimeMillis();
+        List<CompletableFuture<Object>> futures = new ArrayList<>();
+        // 提交每个数据对象进行异步处理
+        for (T data : dataList) {
+            futures.add(CompletableFuture.supplyAsync(() -> processData(data, headerName, headerValue), executorService));
+        }
+        // 收集处理结果
+        List<Object> objList = futures.stream()
+                .map(CompletableFuture::join)
+                .collect(Collectors.toList());
+        // 关闭线程池
+        executorService.shutdown();
+        try {
+            if (!executorService.awaitTermination(2, TimeUnit.MINUTES)) {
+                executorService.shutdownNow();
+            }
+        } catch (InterruptedException e) {
+            executorService.shutdownNow();
+            Thread.currentThread().interrupt();
+        }
+ 
+        log.info("数据处理时间:" + (System.currentTimeMillis() - start) + "ms");
+        return objList;
+    }
+ 
+    /**
+     * 处理单个数据对象以提取相关字段及其值。
+     *
+     * @param data 要处理的数据对象。
+     * @param headerName 用于从对象中提取特定值的标题名称。
+     * @param headerValue 用于从对象中提取特定值的标题值。
+     * @param <T> 数据对象的类型。
+     * @return 表示数据对象的映射。
+     */
+    private static <T> Object processData(T data, String headerName, String headerValue) {
+        Map<String, Object> dataMap = new HashMap<>();
+        Field[] fields = ReflectUtil.getFields(data.getClass());
+ 
+        for (Field field : fields) {
+            field.setAccessible(true);
+            try {
+                // 将字段名和值放入dataMap中
+                dataMap.put(field.getName(), field.get(data));
+                ExcelCollection excelCollection = field.getAnnotation(ExcelCollection.class);
+ 
+                // 如果字段是ExcelCollection并且是List类型,进一步处理
+                if (excelCollection != null && field.getType().getName().equals(List.class.getName())) {
+                    List<?> dynamicColl = (List<?>) field.get(data);
+ 
+                    for (Object arr : dynamicColl) {
+                        String key = null;
+                        String val = null;
+                        Field[] typeFields = arr.getClass().getDeclaredFields();
+ 
+                        for (Field typeField : typeFields) {
+                            typeField.setAccessible(true);
+                            Excel excelItem = typeField.getAnnotation(Excel.class);
+                            // 根据注解提取key和value
+                            if (excelItem != null) {
+                                if (headerName.equals(typeField.getName())) {
+                                    key = String.valueOf(typeField.get(arr));
+                                } else if (headerValue.equals(typeField.getName())) {
+                                    val = String.valueOf(typeField.get(arr));
+                                }
+                            }
+                        }
+                        dataMap.put(key, val);
+                    }
+                }
+            } catch (IllegalAccessException e) {
+                log.error("无法访问字段值:", e.getMessage());
+                return null;
+            }
+        }
+ 
+        return dataMap;
+    }
+ 
+ 
+    /**
+     * 动态生成并下载Excel文件。
+     *
+     * @param response HttpServletResponse对象,用于发送响应。
+     * @param entityList Excel导出实体列表。
+     * @param list 数据列表。
+     * @param fileName 文件名。
+     * @param title 标题。
+     * @param sheetName 工作表名称。
+     * @throws Exception 可能抛出的异常。
+     */
+    public static void downloadExcelEntityDynamic(HttpServletResponse response, List<ExcelExportEntity> entityList,
+                                                  List<Object> list, String fileName, String title,
+                                                  String sheetName) throws Exception {
+        setResponseHeadersForDownload1(response, fileName);
+        ExportParams exportParams = StringUtils.hasText(title) ? new ExportParams(title, sheetName) : new ExportParams();
+        if (!StringUtils.hasText(title)) {
+            exportParams.setSheetName(sheetName);
+        }
+
+        int pageSize = 12000;
+        int totalPages = (list.size() + pageSize - 1) / pageSize;
+        Workbook workbook = ExcelExportUtil.exportBigExcel(exportParams, entityList, (obj, page) -> {
+            if (((int) obj) < page) {
+                return null;
+            }
+            log.info("当前查询第{}页数据", page);
+            int fromIndex = (page - 1) * pageSize;
+            int toIndex = Math.min(page * pageSize, list.size());
+            return list.subList(fromIndex, toIndex);
+        }, totalPages);
+
+        try (ServletOutputStream output = response.getOutputStream();
+             BufferedOutputStream bufferedOutPut = new BufferedOutputStream(output)) {
+            workbook.write(bufferedOutPut);
+            bufferedOutPut.flush();
+        }
+    }
+ 
+    private static void setResponseHeadersForDownload1(HttpServletResponse response, String fileName) throws UnsupportedEncodingException {
+        response.setCharacterEncoding("UTF-8");
+        response.setContentType("application/vnd.ms-excel");
+        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xls");
+    }
+ 
+ 
+    public static void setResponseHeadersForDownload(HttpServletResponse response, String fileName) throws UnsupportedEncodingException {
+        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".xlsx", "utf-8"));
+        response.setContentType("application/octet-stream;charset=utf-8");
+    }
+ 
+    /**
+     * 将@Excel或者@ExcelCollection修饰的字段转为ExcelExportEntity
+     */
+    private static ExcelExportEntity createExcelExportEntity(Field typeField, String name, String key, int index) {
+        Map<String, Object> annotationValueMap = AnnotationUtil.getAnnotationValueMap(typeField, Excel.class);
+        ExcelExportEntity entity = JSONObject.parseObject(JSONObject.toJSONBytes(annotationValueMap), ExcelExportEntity.class);
+        // 字段名和@Excel的name一致,视为动态表头列
+        entity.setName(name);
+        entity.setKey(key);
+        entity.setOrderNum(index);
+        return entity;
+    }
+ 
+ 
+    private static ExcelExportEntity createExcelExportEntity1(Field typeField, String name, String key, int index) {
+        ExcelExportEntity entity = new ExcelExportEntity();
+        // 设置基本信息
+        entity.setName(name);
+        entity.setKey(key);
+        entity.setOrderNum(index);
+        // 设置注解中的属性
+        Excel excel = typeField.getAnnotation(Excel.class);
+        if (excel != null) {
+            try {
+                BeanUtils.copyProperties(entity, excel); // 复制注解中的属性到ExcelExportEntity对象
+            } catch (Exception e) {
+                log.error(e.getMessage());
+                throw new RuntimeException("无法从Excel注解复制属性:", e);
+            }
+        }
+ 
+        return entity;
+    }
+ 
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/LocationTypeListByProjectVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/LocationTypeListByProjectVO.java
index 2583fa4..58fb9d3 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/LocationTypeListByProjectVO.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/LocationTypeListByProjectVO.java
@@ -1,5 +1,6 @@
 package com.ruoyi.system.vo.system;
 
+import cn.afterturn.easypoi.excel.annotation.Excel;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -11,8 +12,10 @@
 public class LocationTypeListByProjectVO {
 
     @ApiModelProperty("类型名称")
+    @Excel(name = "locationTypeName", width = 20)
     private String locationTypeName;
     @ApiModelProperty("数量")
+    @Excel(name = "locationNum", width = 20)
     private Integer locationNum;
     @ApiModelProperty("类型id")
     private String id;
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/ProjectDeptDetailsChildVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/ProjectDeptDetailsChildVO.java
index 37f572e..65f056f 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/ProjectDeptDetailsChildVO.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/ProjectDeptDetailsChildVO.java
@@ -1,5 +1,7 @@
 package com.ruoyi.system.vo.system;
 
+import cn.afterturn.easypoi.excel.annotation.Excel;
+import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
 import com.ruoyi.system.model.TProjectDept;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -12,14 +14,20 @@
 @ApiModel(value = "片区VO")
 public class ProjectDeptDetailsChildVO  {
 
-    @ApiModelProperty("作业类型分类")
-    private List<LocationTypeListByProjectVO> locationTypeList;
     @ApiModelProperty("片区名称")
+    @Excel(name = "片区名称", width = 20)
     private String projectChildName;
+    @ApiModelProperty("保洁员数量")
+    @Excel(name = "巡检保洁员数", width = 20)
+    private Integer cleanerCount;
+    @ApiModelProperty("作业类型分类")
+    @ExcelCollection(name = "作业类型分类")
+    private List<LocationTypeListByProjectVO> locationTypeList;
+    @ApiModelProperty("任务情况汇总")
+    @ExcelCollection(name = "任务情况汇总")
+    private List<TaskSummaryVO> taskSummaryVOS;
     @ApiModelProperty("片区id")
     private String projectId;
-    @ApiModelProperty("保洁员数量")
-    private Integer cleanerCount;
     @ApiModelProperty("总数")
     private Integer total;
     @ApiModelProperty("清洁合格")
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/ProjectDeptDetailsVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/ProjectDeptDetailsVO.java
index d406a10..9fcdd20 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/ProjectDeptDetailsVO.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/ProjectDeptDetailsVO.java
@@ -1,5 +1,7 @@
 package com.ruoyi.system.vo.system;
 
+import cn.afterturn.easypoi.excel.annotation.Excel;
+import cn.afterturn.easypoi.excel.annotation.ExcelCollection;
 import com.ruoyi.system.model.TProjectDept;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -11,9 +13,11 @@
 @ApiModel(value = "任务报表VO")
 public class ProjectDeptDetailsVO  {
 
-    @ApiModelProperty("片区")
-    private List<ProjectDeptDetailsChildVO> projectChild;
-    @ApiModelProperty("项目名称")
+    @ApiModelProperty("项目")
+    @Excel(name = "项目", width = 20,needMerge = true)
     private String projectName;
+    @ApiModelProperty("片区")
+    @ExcelCollection(name = "片区")
+    private List<ProjectDeptDetailsChildVO> projectChild;
 
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/TaskSummaryVO.java b/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/TaskSummaryVO.java
new file mode 100644
index 0000000..6c53153
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/vo/system/TaskSummaryVO.java
@@ -0,0 +1,39 @@
+package com.ruoyi.system.vo.system;
+
+import cn.afterturn.easypoi.excel.annotation.Excel;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+@ApiModel(value = "任务情况汇总VO")
+public class TaskSummaryVO implements Serializable {
+
+    @ApiModelProperty("总数")
+    @Excel(name = "总数")
+    private Integer total;
+    @ApiModelProperty("清洁合格")
+    @Excel(name = "清洁合格")
+    private Integer num1;
+    @ApiModelProperty("清洁不合格")
+    @Excel(name = "清洁不合格")
+    private Integer num2;
+    @ApiModelProperty("未执行任务")
+    @Excel(name = "未执行任务")
+    private Integer num3;
+    @ApiModelProperty("待整改任务")
+    @Excel(name = "待整改任务")
+    private Integer num4;
+    @ApiModelProperty("审核通过任务")
+    @Excel(name = "审核通过任务")
+    private Integer num5;
+    @ApiModelProperty("超时未执行任务")
+    @Excel(name = "超时未执行任务")
+    private Integer num6;
+    @ApiModelProperty("整改完成任务")
+    @Excel(name = "整改完成任务")
+    private Integer num7;
+
+}

--
Gitblit v1.7.1