zlwu hace 1 año
padre
commit
582031ac14

+ 29 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgDormRoomController.java

@@ -1,6 +1,7 @@
 package org.dromara.xg.controller;
 
 import java.util.List;
+import java.util.HashMap;
 
 import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
@@ -21,6 +22,7 @@ import org.dromara.xg.domain.vo.XgDormRoomVo;
 import org.dromara.xg.domain.bo.XgDormRoomBo;
 import org.dromara.xg.service.IXgDormRoomService;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
+import java.util.Map;
 
 /**
  * 宿舍房间信息
@@ -77,6 +79,33 @@ public class XgDormRoomController extends BaseController {
         ExcelUtil.exportExcel(list, "宿舍房间信息", XgDormRoomVo.class, response);
     }
 
+    /**
+     * 导出住宿清单(宿舍卡片式布局)
+     */
+    @SaCheckPermission("xgss:dormRoom:export")
+    @Log(title = "住宿清单", businessType = BusinessType.EXPORT)
+    @PostMapping("/exportAccommodationList")
+    public void exportAccommodationList(
+            @RequestParam(required = false) Long buildingId,
+            @RequestParam(required = false) String buildingName,
+            @RequestParam(required = false) String buildingType,
+            @RequestParam(required = false) String accommodationData,
+            HttpServletResponse response) {
+
+        // 构建参数Map
+        Map<String, Object> params = new HashMap<>();
+        if (buildingId != null)
+            params.put("buildingId", buildingId);
+        if (buildingName != null)
+            params.put("buildingName", buildingName);
+        if (buildingType != null)
+            params.put("buildingType", buildingType);
+        if (accommodationData != null)
+            params.put("accommodationData", accommodationData);
+
+        xgDormRoomService.exportAccommodationList(params, response);
+    }
+
     /**
      * 获取宿舍房间信息详细信息
      *

+ 10 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgDormRoomService.java

@@ -7,6 +7,8 @@ import org.dromara.common.mybatis.core.page.PageQuery;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
+import jakarta.servlet.http.HttpServletResponse;
 
 /**
  * 宿舍房间信息Service接口
@@ -84,4 +86,12 @@ public interface IXgDormRoomService {
      * @return 是否删除成功
      */
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 导出住宿清单(宿舍卡片式布局)
+     *
+     * @param params   导出参数
+     * @param response HTTP响应对象
+     */
+    void exportAccommodationList(Map<String, Object> params, HttpServletResponse response);
 }

+ 280 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRoomServiceImpl.java

@@ -26,6 +26,24 @@ import java.util.Map;
 import java.util.Collection;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.Font;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.apache.poi.ss.util.CellRangeAddress;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.dromara.xg.domain.XgDormBed;
+import org.dromara.xg.mapper.XgDormBedMapper;
+import java.io.IOException;
+import java.io.OutputStream;
 
 /**
  * 宿舍房间信息Service业务层处理
@@ -41,6 +59,7 @@ public class XgDormRoomServiceImpl implements IXgDormRoomService {
     private final XgDormRoomMapper baseMapper;
     private final XgDormBedStudentMapper xgDormBedStudentMapper;
     private final XgSysDeptMapper xgSysDeptMapper;
+    private final XgDormBedMapper xgDormBedMapper;
 
     /**
      * 查询宿舍房间信息
@@ -312,4 +331,265 @@ public class XgDormRoomServiceImpl implements IXgDormRoomService {
             return Set.of();
         }
     }
+
+    /**
+     * 导出住宿清单(宿舍卡片式布局)
+     *
+     * @param params   导出参数
+     * @param response HTTP响应对象
+     */
+    @Override
+    public void exportAccommodationList(Map<String, Object> params, HttpServletResponse response) {
+        try {
+            // 解析参数
+            Long buildingId = Long.valueOf(params.get("buildingId").toString());
+            String buildingName = params.get("buildingName").toString();
+            String buildingType = params.get("buildingType").toString();
+            String buildingTypeText = "1".equals(buildingType) ? "男生楼" : "女生楼";
+
+            // 解析JSON字符串
+            String accommodationDataJson = params.get("accommodationData").toString();
+            ObjectMapper objectMapper = new ObjectMapper();
+            @SuppressWarnings("unchecked")
+            Map<String, Object> accommodationData = objectMapper.readValue(accommodationDataJson, Map.class);
+
+            @SuppressWarnings("unchecked")
+            Map<String, Object> buildingInfo = (Map<String, Object>) accommodationData.get("buildingInfo");
+
+            @SuppressWarnings("unchecked")
+            List<Map<String, Object>> floorDataList = (List<Map<String, Object>>) accommodationData.get("floorData");
+
+            // 创建Excel工作簿
+            try (XSSFWorkbook workbook = new XSSFWorkbook()) {
+                XSSFSheet sheet = workbook.createSheet(buildingName + "住宿清单");
+
+                // 创建样式
+                CellStyle titleStyle = createTitleStyle(workbook);
+                CellStyle headerStyle = createHeaderStyle(workbook);
+                CellStyle dataStyle = createDataStyle(workbook);
+                CellStyle roomNameStyle = createRoomNameStyle(workbook);
+
+                int currentRow = 0;
+
+                // 添加标题
+                XSSFRow titleRow = sheet.createRow(currentRow++);
+                XSSFCell titleCell = titleRow.createCell(0);
+                titleCell.setCellValue(buildingName + " " + buildingTypeText + " 住宿清单");
+                titleCell.setCellStyle(titleStyle);
+                sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 19)); // 合并20列
+                currentRow++; // 空行
+
+                // 处理每个楼层
+                for (Map<String, Object> floorData : floorDataList) {
+                    Integer floorNum = (Integer) floorData.get("floorNum");
+                    @SuppressWarnings("unchecked")
+                    List<Map<String, Object>> rooms = (List<Map<String, Object>>) floorData.get("rooms");
+
+                    if (rooms == null || rooms.isEmpty()) {
+                        continue;
+                    }
+
+                    // 添加楼层标题
+                    XSSFRow floorRow = sheet.createRow(currentRow++);
+                    XSSFCell floorCell = floorRow.createCell(0);
+                    floorCell.setCellValue("第" + floorNum + "层");
+                    floorCell.setCellStyle(headerStyle);
+                    sheet.addMergedRegion(new CellRangeAddress(currentRow - 1, currentRow - 1, 0, 19));
+                    currentRow++; // 空行
+
+                    // 按4个宿舍一行进行分组处理
+                    for (int i = 0; i < rooms.size(); i += 4) {
+                        int endIndex = Math.min(i + 4, rooms.size());
+                        List<Map<String, Object>> roomGroup = rooms.subList(i, endIndex);
+
+                        // 处理当前组的宿舍
+                        currentRow = processRoomGroup(sheet, roomGroup, currentRow, roomNameStyle, headerStyle,
+                                dataStyle);
+                        currentRow += 2; // 组间空行
+                    }
+                }
+
+                // 设置列宽 (4个宿舍,每个宿舍4列,宿舍间3个空列,共19列)
+                for (int i = 0; i < 20; i++) {
+                    sheet.setColumnWidth(i, 15 * 256); // 15个字符宽度
+                }
+
+                // 设置响应头
+                String fileName = buildingName + "_住宿清单_" + System.currentTimeMillis() + ".xlsx";
+                response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+                response.setHeader("Content-Disposition",
+                        "attachment; filename=\"" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1") + "\"");
+
+                // 输出Excel文件
+                try (OutputStream outputStream = response.getOutputStream()) {
+                    workbook.write(outputStream);
+                    outputStream.flush();
+                }
+            }
+
+        } catch (Exception e) {
+            log.error("导出住宿清单失败", e);
+            throw new RuntimeException("导出住宿清单失败", e);
+        }
+    }
+
+    /**
+     * 处理一组宿舍(最多4个)
+     */
+    private int processRoomGroup(XSSFSheet sheet, List<Map<String, Object>> roomGroup, int startRow,
+            CellStyle roomNameStyle, CellStyle headerStyle, CellStyle dataStyle) {
+        int currentRow = startRow;
+
+        // 宿舍名称行
+        XSSFRow roomNameRow = sheet.createRow(currentRow++);
+        for (int i = 0; i < roomGroup.size(); i++) {
+            Map<String, Object> room = roomGroup.get(i);
+            String roomName = (String) room.get("roomName");
+
+            // 每个宿舍占4列,宿舍之间增加1列间隔 (i * 5)
+            int startCol = i * 5;
+
+            // 为整个合并区域的所有单元格创建并设置样式
+            for (int col = startCol; col <= startCol + 3; col++) {
+                XSSFCell cell = roomNameRow.createCell(col);
+                cell.setCellStyle(roomNameStyle);
+                if (col == startCol) {
+                    cell.setCellValue(roomName); // 只在第一个单元格设置值
+                }
+            }
+
+            sheet.addMergedRegion(new CellRangeAddress(currentRow - 1, currentRow - 1, startCol, startCol + 3));
+        }
+
+        // 表头行
+        XSSFRow headerRow = sheet.createRow(currentRow++);
+        String[] headers = { "床位", "姓名", "班级", "备注" };
+        for (int i = 0; i < roomGroup.size(); i++) {
+            for (int j = 0; j < headers.length; j++) {
+                // 每个宿舍占4列,宿舍之间增加1列间隔 (i * 5)
+                XSSFCell headerCell = headerRow.createCell(i * 5 + j);
+                headerCell.setCellValue(headers[j]);
+                headerCell.setCellStyle(headerStyle);
+            }
+        }
+
+        // 计算最大床位数
+        int maxBeds = roomGroup.stream()
+                .mapToInt(room -> {
+                    @SuppressWarnings("unchecked")
+                    List<Map<String, Object>> beds = (List<Map<String, Object>>) room.get("beds");
+                    return beds != null ? beds.size() : 0;
+                })
+                .max()
+                .orElse(0);
+
+        // 床位数据行
+        for (int bedIndex = 0; bedIndex < maxBeds; bedIndex++) {
+            XSSFRow dataRow = sheet.createRow(currentRow++);
+
+            for (int roomIndex = 0; roomIndex < roomGroup.size(); roomIndex++) {
+                Map<String, Object> room = roomGroup.get(roomIndex);
+                @SuppressWarnings("unchecked")
+                List<Map<String, Object>> beds = (List<Map<String, Object>>) room.get("beds");
+
+                if (beds != null && bedIndex < beds.size()) {
+                    Map<String, Object> bed = beds.get(bedIndex);
+
+                    // 每个宿舍占4列,宿舍之间增加1列间隔 (roomIndex * 5)
+                    // 床位
+                    XSSFCell bedNameCell = dataRow.createCell(roomIndex * 5);
+                    bedNameCell.setCellValue((String) bed.get("bedName"));
+                    bedNameCell.setCellStyle(dataStyle);
+
+                    // 姓名
+                    XSSFCell studentNameCell = dataRow.createCell(roomIndex * 5 + 1);
+                    studentNameCell.setCellValue((String) bed.get("studentName"));
+                    studentNameCell.setCellStyle(dataStyle);
+
+                    // 班级
+                    XSSFCell classNameCell = dataRow.createCell(roomIndex * 5 + 2);
+                    classNameCell.setCellValue((String) bed.get("className"));
+                    classNameCell.setCellStyle(dataStyle);
+
+                    // 备注
+                    XSSFCell remarkCell = dataRow.createCell(roomIndex * 5 + 3);
+                    remarkCell.setCellValue((String) bed.get("bedRemark"));
+                    remarkCell.setCellStyle(dataStyle);
+                } else {
+                    // 空床位
+                    for (int j = 0; j < 4; j++) {
+                        XSSFCell emptyCell = dataRow.createCell(roomIndex * 5 + j);
+                        emptyCell.setCellValue("");
+                        emptyCell.setCellStyle(dataStyle);
+                    }
+                }
+            }
+        }
+
+        return currentRow;
+    }
+
+    /**
+     * 创建标题样式
+     */
+    private CellStyle createTitleStyle(XSSFWorkbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+        Font font = workbook.createFont();
+        font.setBold(true);
+        font.setFontHeightInPoints((short) 16);
+        style.setFont(font);
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        return style;
+    }
+
+    /**
+     * 创建表头样式
+     */
+    private CellStyle createHeaderStyle(XSSFWorkbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+        Font font = workbook.createFont();
+        font.setBold(true);
+        font.setFontHeightInPoints((short) 12);
+        style.setFont(font);
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+        return style;
+    }
+
+    /**
+     * 创建数据样式
+     */
+    private CellStyle createDataStyle(XSSFWorkbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+        return style;
+    }
+
+    /**
+     * 创建宿舍名称样式
+     */
+    private CellStyle createRoomNameStyle(XSSFWorkbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+        Font font = workbook.createFont();
+        font.setBold(true);
+        font.setFontHeightInPoints((short) 14);
+        style.setFont(font);
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+        return style;
+    }
 }

+ 2 - 2
web/src/views/xg/xgss/dormHygieneCheck/dormHygieneCheck/index.vue

@@ -76,11 +76,11 @@
               >修改</el-button
             >
           </el-col>
-          <el-col :span="1.5">
+          <!-- <el-col :span="1.5">
             <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['dormHygieneCheck:dormHygieneCheck:export']"
               >导出</el-button
             >
-          </el-col>
+          </el-col> -->
           <right-toolbar v-model="showSearch" @queryTable="getList"></right-toolbar>
         </el-row>
       </template>

+ 99 - 2
web/src/views/xg/xgss/xsssfpejxy/index.vue

@@ -135,6 +135,7 @@
                   <el-button type="primary" icon="check" size="small" @click="handleAddRoom">分配给二级学院</el-button>
                   <el-button type="warning" icon="RefreshLeft" size="small" @click="handleBatchRecall">批量收回宿舍</el-button>
                   <el-button type="danger" icon="UserFilled" size="small" @click="handleBatchStayaway">批量退宿床位</el-button>
+                  <el-button type="success" icon="Download" size="small" @click="handleExportAccommodationList">导出住宿清单</el-button>
                 </el-col>
                 <el-col :span="12" style="text-align: right">
                   <el-button
@@ -358,7 +359,7 @@
 
 <script setup name="DormRoomTreeManagement" lang="ts">
 import { ref, computed, reactive, onMounted, onUnmounted, nextTick, watch, getCurrentInstance } from 'vue';
-import { InfoFilled, House, Avatar, UserFilled } from '@element-plus/icons-vue';
+import { InfoFilled, House, Avatar, UserFilled, Download } from '@element-plus/icons-vue';
 import type { DormTreeNode, BedInfo } from '@/api/xg/xgss/xsssfjgl/types';
 import type { DormRoomVO } from '@/api/xg/xgss/dormRoom/types';
 import type { DormBedDetailVO } from '@/api/xg/xgss/dormBed/types';
@@ -376,7 +377,7 @@ import BedDialog from './components/BedDialog.vue';
 import CollegeAllocationDialog from './components/CollegeAllocationDialog.vue';
 import { batchRecallDormBeds, classTeacherStayawaySelectedBeds } from '@/api/xg/xgss/dormBedStudent/dormBedStudent';
 
-// const { proxy } = getCurrentInstance();
+const { proxy } = getCurrentInstance() as any;
 
 // 组件状态
 const dormTreeRef = ref();
@@ -775,6 +776,102 @@ const handleBatchStayaway = async () => {
   }
 };
 
+/** 导出住宿清单 */
+const handleExportAccommodationList = async () => {
+  if (!selectedBuilding.value) {
+    ElMessage.warning('请先选择宿舍楼');
+    return;
+  }
+
+  if (!roomList.value || roomList.value.length === 0) {
+    ElMessage.warning('当前宿舍楼没有房间数据');
+    return;
+  }
+
+  try {
+    // 创建Excel工作簿数据
+    const accommodationData = generateAccommodationExcelData();
+
+    // 使用proxy.download方法导出Excel
+    const filename = `${selectedBuilding.value.label}_住宿清单_${new Date().getTime()}.xlsx`;
+
+    proxy?.download(
+      'xgss/dormRoom/exportAccommodationList',
+      {
+        buildingId: selectedBuilding.value.id,
+        buildingName: selectedBuilding.value.label,
+        buildingType: selectedBuilding.value.buildingType,
+        accommodationData: JSON.stringify(accommodationData) // 转换为JSON字符串
+      },
+      filename
+    );
+
+    ElMessage.success('导出任务已提交');
+  } catch (error) {
+    console.error('导出住宿清单失败:', error);
+    ElMessage.error('导出失败,请重试');
+  }
+};
+
+/** 生成住宿清单Excel数据 */
+const generateAccommodationExcelData = () => {
+  // 按楼层分组房间数据
+  const floorGroups = new Map<number, any[]>();
+
+  roomList.value.forEach((room) => {
+    const floorNum = room.floorNum || 1;
+    if (!floorGroups.has(floorNum)) {
+      floorGroups.set(floorNum, []);
+    }
+
+    // 为每个房间准备数据,包括床位信息
+    const roomData = {
+      roomId: room.id,
+      roomName: room.roomName,
+      floorNum: room.floorNum,
+      roomType: room.roomType,
+      roomStatus: room.roomStatus,
+      remark: room.remark || '',
+      beds: room.children
+        ? room.children.map((bed: any) => ({
+            bedId: bed.id,
+            bedName: bed.bedName,
+            bedType: bed.bedType,
+            bedStatus: bed.bedStatus,
+            studentNo: bed.studentNo || '',
+            studentName: bed.studentName || '',
+            collegeName: bed.collegeName || '',
+            majorName: bed.majorName || '',
+            className: bed.className || '',
+            studentGender: bed.studentGender,
+            bedRemark: bed.bedRemark || ''
+          }))
+        : []
+    };
+
+    floorGroups.get(floorNum)!.push(roomData);
+  });
+
+  // 按楼层排序
+  const sortedFloors = Array.from(floorGroups.keys()).sort((a, b) => a - b);
+
+  // 构建Excel数据结构
+  const excelData = {
+    buildingInfo: {
+      buildingId: selectedBuilding.value?.id,
+      buildingName: selectedBuilding.value?.label,
+      buildingType: selectedBuilding.value?.buildingType,
+      buildingTypeText: selectedBuilding.value?.buildingType === '1' ? '男生楼' : '女生楼'
+    },
+    floorData: sortedFloors.map((floorNum) => ({
+      floorNum,
+      rooms: floorGroups.get(floorNum) || []
+    }))
+  };
+
+  return excelData;
+};
+
 /** 全部展开 */
 const expandAll = async () => {
   if (expandAllLoading.value || selectionLoading.value || !roomList.value?.length) return;