提交宿舍区去除的业务修改

This commit is contained in:
zlwu committed 2025-09-04 12:29:51 +08:00
1 parent 99b771bb51
commit 2a38245ae2
24 files changed
+1139 -557

No files matched your search

@@ -157,7 +157,7 @@ public class XgDormBedStudentController extends BaseController {
/**
* 获取当前用户二级学院的宿舍楼树形结构
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
* 校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
*/
@SaCheckPermission("dormBedStudent:dormBedStudent:list")
@GetMapping("/currentUserCollegeDormTree")
@@ -168,7 +168,7 @@ public class XgDormBedStudentController extends BaseController {
/**
* 获取班主任管理的班级宿舍楼树形结构
* 只显示班主任管理班级分配到的宿舍楼
* 校区-宿舍楼二级结构,只显示班主任管理班级分配到的宿舍楼
*/
@SaCheckPermission("dormBedStudent:dormBedStudent:list")
@GetMapping("/getClassTeacherDormTree")
@@ -18,6 +18,7 @@ import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.xg.domain.vo.XgDormBuildingVo;
import org.dromara.xg.domain.vo.XgDormBuildingSimplifiedVo;
import org.dromara.xg.domain.bo.XgDormBuildingBo;
import org.dromara.xg.domain.vo.BuildingTreeSelectVo;
import org.dromara.xg.service.IXgDormBuildingService;
@@ -104,7 +105,7 @@ public class XgDormBuildingController extends BaseController {
/**
* 获取宿舍楼树形结构数据
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
* 校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
*/
@SaCheckPermission("sslxx:dormBuilding:list")
@GetMapping("/buildingTree")
@@ -112,4 +113,25 @@ public class XgDormBuildingController extends BaseController {
List<BuildingTreeSelectVo> treeData = xgDormBuildingService.getBuildingTreeData();
return R.ok(treeData);
}
/**
* 查询宿舍楼信息简化列表
* 用于简化界面显示,隐藏宿舍区层级,直接显示校区→宿舍楼结构
*/
@SaCheckPermission("sslxx:dormBuilding:list")
@GetMapping("/simplifiedList")
public TableDataInfo<XgDormBuildingSimplifiedVo> getSimplifiedList(XgDormBuildingBo bo, PageQuery pageQuery) {
return xgDormBuildingService.querySimplifiedPageList(bo, pageQuery);
}
/**
* 查询宿舍楼信息简化列表(不分页)
* 用于下拉选择等场景
*/
@SaCheckPermission("sslxx:dormBuilding:list")
@GetMapping("/simplifiedListAll")
public R<List<XgDormBuildingSimplifiedVo>> getSimplifiedListAll(XgDormBuildingBo bo) {
List<XgDormBuildingSimplifiedVo> list = xgDormBuildingService.querySimplifiedList(bo);
return R.ok(list);
}
}
@@ -9,6 +9,7 @@ import org.springframework.validation.annotation.Validated;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.core.domain.R;
import org.dromara.xg.domain.vo.DormBuildingTreeVo;
import org.dromara.xg.domain.vo.DormBuildingTreeSimplifiedVo;
import org.dromara.xg.domain.bo.DormBuildingTreeBo;
import org.dromara.xg.service.IXgDormBuildingTreeService;
@@ -28,24 +29,70 @@ public class XgDormBuildingTreeController extends BaseController {
/**
* 获取宿舍楼树形结构数据
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
* 校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
*/
@SaCheckPermission("xgss:dormBuildingTree:list")
@GetMapping("/list")
public R<List<DormBuildingTreeVo>> getDormBuildingTree(DormBuildingTreeBo bo) {
List<DormBuildingTreeVo> treeData = xgDormBuildingTreeService.getDormBuildingTree(bo);
public R<List<DormBuildingTreeSimplifiedVo>> getDormBuildingTree(DormBuildingTreeBo bo) {
List<DormBuildingTreeSimplifiedVo> treeData = xgDormBuildingTreeService.getSimplifiedDormBuildingTree(bo);
return R.ok(treeData);
}
/**
* 获取所有宿舍楼树形结构数据(不考虑权限)
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
* 校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
*/
@SaCheckPermission("xgss:dormBuildingTree:list")
@GetMapping("/listAll")
public R<List<DormBuildingTreeVo>> getAllDormBuildingTree(DormBuildingTreeBo bo) {
public R<List<DormBuildingTreeSimplifiedVo>> getAllDormBuildingTree(DormBuildingTreeBo bo) {
List<DormBuildingTreeSimplifiedVo> treeData = xgDormBuildingTreeService.getAllSimplifiedDormBuildingTree(bo);
return R.ok(treeData);
}
/**
* 获取宿舍楼树形结构完整数据(包含宿舍区层级)
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
* 注意:此接口仅用于特殊需求,一般情况请使用/list接口
*/
@SaCheckPermission("xgss:dormBuildingTree:list")
@GetMapping("/fullList")
public R<List<DormBuildingTreeVo>> getFullDormBuildingTree(DormBuildingTreeBo bo) {
List<DormBuildingTreeVo> treeData = xgDormBuildingTreeService.getDormBuildingTree(bo);
return R.ok(treeData);
}
/**
* 获取所有宿舍楼树形结构完整数据(不考虑权限,包含宿舍区层级)
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
* 注意:此接口仅用于特殊需求,一般情况请使用/listAll接口
*/
@SaCheckPermission("xgss:dormBuildingTree:list")
@GetMapping("/fullListAll")
public R<List<DormBuildingTreeVo>> getAllFullDormBuildingTree(DormBuildingTreeBo bo) {
List<DormBuildingTreeVo> treeData = xgDormBuildingTreeService.getAllDormBuildingTree(bo);
return R.ok(treeData);
}
/**
* 获取宿舍楼树形结构简化数据
* 校区-宿舍楼二级结构,隐藏宿舍区层级,校区不可选,只有宿舍楼可以选择
*/
@SaCheckPermission("xgss:dormBuildingTree:list")
@GetMapping("/simplifiedList")
public R<List<DormBuildingTreeSimplifiedVo>> getSimplifiedDormBuildingTree(DormBuildingTreeBo bo) {
List<DormBuildingTreeSimplifiedVo> treeData = xgDormBuildingTreeService.getSimplifiedDormBuildingTree(bo);
return R.ok(treeData);
}
/**
* 获取所有宿舍楼树形结构简化数据(不考虑权限)
* 校区-宿舍楼二级结构,隐藏宿舍区层级,校区不可选,只有宿舍楼可以选择
*/
@SaCheckPermission("xgss:dormBuildingTree:list")
@GetMapping("/simplifiedListAll")
public R<List<DormBuildingTreeSimplifiedVo>> getAllSimplifiedDormBuildingTree(DormBuildingTreeBo bo) {
List<DormBuildingTreeSimplifiedVo> treeData = xgDormBuildingTreeService.getAllSimplifiedDormBuildingTree(bo);
return R.ok(treeData);
}
}
@@ -111,7 +111,7 @@ public class XgDormHygieneCheck extends TenantEntity {
private String campusName;
/**
* 宿舍总分(0-100分)
* 宿舍扣分(0-100分)
*/
private Double totalScore;
@@ -94,7 +94,7 @@ public class XgDormHygieneStudentScore extends TenantEntity {
private String bedCode;
/**
* 学生个人得分(0-100分)
* 学生个人扣分(0-100分)
*/
private Double studentScore;
@@ -41,7 +41,8 @@ public class XgDormBuildingBo extends BaseEntity {
/**
* 所属宿舍区ID
*/
@NotNull(message = "所属宿舍区ID不能为空", groups = { AddGroup.class, EditGroup.class })
// @NotNull(message = "所属宿舍区ID不能为空", groups = { AddGroup.class, EditGroup.class
// }) // 宿舍区轻量化改造:移除必填验证
private Long areaId;
/**
@@ -0,0 +1,104 @@
package org.dromara.xg.domain.vo;
import java.io.Serializable;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
/**
* 宿舍楼树形结构简化VO
* 用于简化界面显示,只显示校区→宿舍楼的二级结构,隐藏宿舍区层级
*
* @author wuzilong
* @date 2025-01-03
*/
@Data
public class DormBuildingTreeSimplifiedVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 节点ID
*/
private Long id;
/**
* 节点名称
*/
private String label;
/**
* 节点类型 (campus:校区, building:宿舍楼)
* 注意:简化版本中移除了 area:宿舍区 类型
*/
private String type;
/**
* 是否禁用节点(校区节点禁用点击,宿舍楼节点可点击)
*/
private Boolean disabled;
/**
* 宿舍楼层数(仅宿舍楼节点有此字段)
*/
private Integer buildingFloors;
/**
* 宿舍楼类型(1男生楼 2女生楼 3混合楼)(仅宿舍楼节点有此字段)
*/
private String buildingType;
/**
* 子节点
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<DormBuildingTreeSimplifiedVo> children;
/**
* 从完整树形VO转换为简化树形VO
* 将三级结构(校区→宿舍区→宿舍楼)转换为二级结构(校区→宿舍楼)
*/
public static DormBuildingTreeSimplifiedVo fromFullTreeVo(DormBuildingTreeVo fullTreeVo) {
if (fullTreeVo == null) {
return null;
}
DormBuildingTreeSimplifiedVo simplified = new DormBuildingTreeSimplifiedVo();
simplified.setId(fullTreeVo.getId());
simplified.setLabel(fullTreeVo.getLabel());
simplified.setBuildingFloors(fullTreeVo.getBuildingFloors());
simplified.setBuildingType(fullTreeVo.getBuildingType());
// 根据节点类型进行转换
if ("campus".equals(fullTreeVo.getType())) {
// 校区节点:保持不变
simplified.setType("campus");
simplified.setDisabled(true);
// 处理子节点:跳过宿舍区层级,直接提取宿舍楼
simplified.setChildren(flattenAreaChildren(fullTreeVo.getChildren()));
} else if ("building".equals(fullTreeVo.getType())) {
// 宿舍楼节点:保持不变
simplified.setType("building");
simplified.setDisabled(false);
}
return simplified;
}
/**
* 扁平化宿舍区子节点,将宿舍区下的宿舍楼直接提升为校区的子节点
*/
private static List<DormBuildingTreeSimplifiedVo> flattenAreaChildren(List<DormBuildingTreeVo> areaNodes) {
if (areaNodes == null || areaNodes.isEmpty()) {
return null;
}
return areaNodes.stream()
.filter(node -> "area".equals(node.getType())) // 找到宿舍区节点
.flatMap(areaNode -> areaNode.getChildren() != null ? areaNode.getChildren().stream()
: java.util.stream.Stream.empty()) // 提取宿舍楼
.filter(buildingNode -> "building".equals(buildingNode.getType())) // 确保是宿舍楼节点
.map(DormBuildingTreeSimplifiedVo::fromFullTreeVo) // 转换为简化VO
.toList();
}
}
@@ -0,0 +1,103 @@
package org.dromara.xg.domain.vo;
import org.dromara.xg.domain.XgDormBuilding;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.springframework.beans.BeanUtils;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* 宿舍楼信息简化视图对象 xg_dorm_building
* 用于简化界面显示,隐藏宿舍区层级,直接显示校区→宿舍楼结构
*
* @author zlwu
* @date 2025-01-03
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = XgDormBuilding.class)
public class XgDormBuildingSimplifiedVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 宿舍楼编码
*/
@ExcelProperty(value = "宿舍楼编码")
private String buildingCode;
/**
* 宿舍楼名称
*/
@ExcelProperty(value = "宿舍楼名称")
private String buildingName;
/**
* 所属校区ID
*/
@ExcelProperty(value = "所属校区ID")
private Long campusId;
/**
* 校区名称
*/
@ExcelProperty(value = "校区名称")
private String campusName;
/**
* 宿舍楼类型(1男生楼 2女生楼 3混合楼)
*/
@ExcelProperty(value = "宿舍楼类型(1男生楼 2女生楼 3混合楼)", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "dorm_type")
private String buildingType;
/**
* 宿舍楼描述
*/
@ExcelProperty(value = "宿舍楼描述")
private String buildingDescription;
/**
* 排序号
*/
@ExcelProperty(value = "排序号")
private Long sortOrder;
/**
* 总楼层数
*/
@ExcelProperty(value = "总楼层数")
private Integer floors;
// 基础字段
private Long createBy; // 创建者
private Date createTime; // 创建时间
private Long updateBy; // 更新者
private Date updateTime; // 更新时间
/**
* 从完整VO转换为简化VO
*/
public static XgDormBuildingSimplifiedVo fromFullVo(XgDormBuildingVo fullVo) {
if (fullVo == null) {
return null;
}
XgDormBuildingSimplifiedVo simplified = new XgDormBuildingSimplifiedVo();
BeanUtils.copyProperties(fullVo, simplified);
return simplified;
}
}
@@ -0,0 +1,183 @@
package org.dromara.xg.domain.vo;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.xg.domain.XgDormHygieneCheck;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.springframework.beans.BeanUtils;
import java.io.Serial;
import java.io.Serializable;
/**
* 宿舍卫生检查简化视图对象 xg_dorm_hygiene_check
* 用于简化界面显示,隐藏宿舍区层级,直接显示校区→宿舍楼→房间结构
*
* @author zlwu
* @date 2025-01-03
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = XgDormHygieneCheck.class)
public class XgDormHygieneCheckSimplifiedVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@ExcelProperty(value = "主键ID")
private Long id;
/**
* 删除标志(0代表存在 1代表删除)
*/
@ExcelProperty(value = "删除标志", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=代表存在,1=代表删除")
private Long deleted;
/**
* 检查标题
*/
@ExcelProperty(value = "检查标题")
private String checkTitle;
/**
* 检查日期
*/
@ExcelProperty(value = "检查日期")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date checkDate;
/**
* 检查员ID,关联sys_user.id
*/
@ExcelProperty(value = "检查员ID,关联sys_user.id")
private Long checkerId;
/**
* 检查员姓名
*/
@ExcelProperty(value = "检查员姓名")
private String checkerName;
/**
* 检查类型(1日常检查 2专项检查 3突击检查 4复查)
*/
@ExcelProperty(value = "检查类型", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "xg_dorm_hygiene_check_type")
private String checkType;
/**
* 检查级别(1宿管员 2学院 3学校)
*/
@ExcelProperty(value = "检查级别", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "xg_dorm_hygiene_check_level")
private String checkLevel;
/**
* 宿舍房间ID,关联xg_dorm_room.id
*/
@ExcelProperty(value = "宿舍房间ID,关联xg_dorm_room.id")
private Long roomId;
/**
* 房间编号
*/
@ExcelProperty(value = "房间编号")
private String roomCode;
/**
* 房间名称
*/
@ExcelProperty(value = "房间名称")
private String roomName;
/**
* 宿舍楼ID,关联xg_dorm_building.id
*/
@ExcelProperty(value = "宿舍楼ID,关联xg_dorm_building.id")
private Long buildingId;
/**
* 宿舍楼名称
*/
@ExcelProperty(value = "宿舍楼名称")
private String buildingName;
/**
* 校区ID,关联xg_dorm_campus.id
*/
@ExcelProperty(value = "校区ID,关联xg_dorm_campus.id")
private Long campusId;
/**
* 校区名称
*/
@ExcelProperty(value = "校区名称")
private String campusName;
/**
* 宿舍总分(0-100分)
*/
@ExcelProperty(value = "宿舍总分", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=-100分")
private Double totalScore;
/**
* 卫生等级(A优秀 B良好 C合格 D不合格)
*/
@ExcelProperty(value = "卫生等级", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "xg_dorm_hygiene_grade")
private String hygieneGrade;
/**
* 附件OSS ID列表,逗号分隔
*/
@ExcelProperty(value = "附件OSS ID列表,逗号分隔")
private String attachmentIds;
/**
* 检查总结
*/
@ExcelProperty(value = "检查总结")
private String checkSummary;
/**
* 整改建议
*/
@ExcelProperty(value = "整改建议")
private String improvementSuggestions;
/**
* 检查备注
*/
@ExcelProperty(value = "检查备注")
private String checkRemarks;
// 基础字段
private Long createBy; // 创建者
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime; // 创建时间
private Long updateBy; // 更新者
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime; // 更新时间
/**
* 从完整VO转换为简化VO
*/
public static XgDormHygieneCheckSimplifiedVo fromFullVo(XgDormHygieneCheckVo fullVo) {
if (fullVo == null) {
return null;
}
XgDormHygieneCheckSimplifiedVo simplified = new XgDormHygieneCheckSimplifiedVo();
BeanUtils.copyProperties(fullVo, simplified);
return simplified;
}
}
@@ -107,7 +107,7 @@ public interface IXgDormBedStudentService {
/**
* 获取当前用户二级学院的宿舍楼树形结构
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
* 校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
*
* @return 宿舍楼树形结构数据
*/
@@ -1,6 +1,7 @@
package org.dromara.xg.service;
import org.dromara.xg.domain.vo.XgDormBuildingVo;
import org.dromara.xg.domain.vo.XgDormBuildingSimplifiedVo;
import org.dromara.xg.domain.bo.XgDormBuildingBo;
import org.dromara.xg.domain.vo.BuildingTreeSelectVo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -74,4 +75,23 @@ public interface IXgDormBuildingService {
* @return 树形结构数据
*/
List<BuildingTreeSelectVo> getBuildingTreeData();
/**
* 分页查询宿舍楼信息简化列表
* 用于简化界面显示,隐藏宿舍区层级,直接显示校区→宿舍楼结构
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 宿舍楼信息简化分页列表
*/
TableDataInfo<XgDormBuildingSimplifiedVo> querySimplifiedPageList(XgDormBuildingBo bo, PageQuery pageQuery);
/**
* 查询符合条件的宿舍楼信息简化列表
* 用于简化界面显示,隐藏宿舍区层级,直接显示校区→宿舍楼结构
*
* @param bo 查询条件
* @return 宿舍楼信息简化列表
*/
List<XgDormBuildingSimplifiedVo> querySimplifiedList(XgDormBuildingBo bo);
}
@@ -2,6 +2,7 @@ package org.dromara.xg.service;
import java.util.List;
import org.dromara.xg.domain.vo.DormBuildingTreeVo;
import org.dromara.xg.domain.vo.DormBuildingTreeSimplifiedVo;
import org.dromara.xg.domain.bo.DormBuildingTreeBo;
/**
@@ -28,4 +29,22 @@ public interface IXgDormBuildingTreeService {
*/
List<DormBuildingTreeVo> getAllDormBuildingTree(DormBuildingTreeBo bo);
/**
* 获取宿舍楼树形结构简化数据
* 校区-宿舍楼二级结构,隐藏宿舍区层级
*
* @param bo 查询参数
* @return 简化树形结构数据
*/
List<DormBuildingTreeSimplifiedVo> getSimplifiedDormBuildingTree(DormBuildingTreeBo bo);
/**
* 获取所有宿舍楼树形结构简化数据(不考虑权限)
* 校区-宿舍楼二级结构,隐藏宿舍区层级
*
* @param bo 查询参数
* @return 简化树形结构数据
*/
List<DormBuildingTreeSimplifiedVo> getAllSimplifiedDormBuildingTree(DormBuildingTreeBo bo);
}
@@ -983,7 +983,8 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
Long roomId = entry.getKey();
List<Long> roomUnallocatedBeds = entry.getValue();
String roomCode = roomCodeMap.get(roomId);
log.error("房间{}({})有{}个床位未分配:{}", roomCode, roomId, roomUnallocatedBeds.size(), roomUnallocatedBeds);
log.error("房间{}({})有{}个床位未分配:{}", roomCode, roomId, roomUnallocatedBeds.size(),
roomUnallocatedBeds);
}
throw new RuntimeException("床位分配不完整,存在" + (totalExpectedBeds - totalAllocatedBeds) + "个床位未分配");
@@ -1008,7 +1009,7 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
/**
* 获取当前用户二级学院的宿舍楼树形结构
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
* 校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
*
* @return 宿舍楼树形结构数据
*/
@@ -1159,8 +1160,8 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
.orderByAsc(XgDormCampus::getSortOrder, XgDormCampus::getId);
List<XgDormCampus> campuses = xgDormCampusMapper.selectList(campusWrapper);
// 构建树形结构
return buildTreeStructure(campuses, areas, buildings);
// 构建简化版树形结构(校区-宿舍楼,去除宿舍区层级)
return buildSimplifiedTreeStructure(campuses, areas, buildings);
} catch (Exception e) {
log.error("构建宿舍楼树形结构失败", e);
@@ -1232,6 +1233,60 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
return new ArrayList<>(campusMap.values());
}
/**
* 构建简化树形结构:校区-宿舍楼(去除宿舍区层级)
*/
private List<DormBuildingTreeVo> buildSimplifiedTreeStructure(List<XgDormCampus> campuses,
List<XgDormArea> areas,
List<XgDormBuilding> buildings) {
// 构建校区节点(第一层)
Map<Long, DormBuildingTreeVo> campusMap = new LinkedHashMap<>();
for (XgDormCampus campus : campuses) {
// 检查该校区下是否有宿舍楼(通过宿舍区关联)
boolean hasBuildings = buildings.stream().anyMatch(building -> areas.stream().anyMatch(
area -> area.getId().equals(building.getAreaId()) && area.getCampusId().equals(campus.getId())));
if (hasBuildings) {
DormBuildingTreeVo campusNode = new DormBuildingTreeVo();
campusNode.setId(campus.getId());
campusNode.setLabel(campus.getCampusName());
campusNode.setType("campus");
campusNode.setDisabled(true); // 校区节点禁止点击
campusNode.setChildren(new ArrayList<>());
campusMap.put(campus.getId(), campusNode);
}
}
// 直接构建宿舍楼节点(第二层),跳过宿舍区
for (XgDormBuilding building : buildings) {
// 通过宿舍区找到对应的校区
XgDormArea area = areas.stream()
.filter(a -> a.getId().equals(building.getAreaId()))
.findFirst()
.orElse(null);
if (area != null) {
DormBuildingTreeVo buildingNode = new DormBuildingTreeVo();
buildingNode.setId(building.getId());
buildingNode.setLabel(building.getBuildingName());
buildingNode.setType("building");
buildingNode.setDisabled(false); // 宿舍楼节点可以点击
buildingNode.setBuildingFloors(building.getFloors());
buildingNode.setBuildingType(building.getBuildingType());
buildingNode.setChildren(new ArrayList<>());
// 直接添加到校区下,跳过宿舍区层级
DormBuildingTreeVo campusNode = campusMap.get(area.getCampusId());
if (campusNode != null) {
campusNode.getChildren().add(buildingNode);
}
}
}
return new ArrayList<>(campusMap.values());
}
/**
* 获取班主任管理的班级宿舍楼树
* 通过当前用户所在的班级,获取该班级分配的宿舍楼
@@ -12,6 +12,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.dromara.xg.domain.bo.XgDormBuildingBo;
import org.dromara.xg.domain.vo.XgDormBuildingVo;
import org.dromara.xg.domain.vo.XgDormBuildingSimplifiedVo;
import org.dromara.xg.domain.XgDormBuilding;
import org.dromara.xg.domain.XgDormArea;
import org.dromara.xg.domain.XgDormCampus;
@@ -145,7 +146,7 @@ public class XgDormBuildingServiceImpl implements IXgDormBuildingService {
/**
* 获取宿舍楼树形结构数据
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
* 校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
*
* @return 宿舍楼树形结构数据
*/
@@ -156,59 +157,41 @@ public class XgDormBuildingServiceImpl implements IXgDormBuildingService {
Wrappers.<XgDormCampus>lambdaQuery()
.orderByAsc(XgDormCampus::getSortOrder, XgDormCampus::getId));
// 2. 获取所有宿舍区数据
List<XgDormArea> areaList = areaMapper.selectList(
Wrappers.<XgDormArea>lambdaQuery()
.orderByAsc(XgDormArea::getSortOrder, XgDormArea::getId));
// 3. 获取所有宿舍楼数据
// 2. 获取所有宿舍楼数据
List<XgDormBuilding> buildingList = baseMapper.selectList(
Wrappers.<XgDormBuilding>lambdaQuery()
.orderByAsc(XgDormBuilding::getSortOrder, XgDormBuilding::getId));
// 4. 构建树形结构
// 3. 构建树形结构
List<BuildingTreeSelectVo> treeList = new ArrayList<>();
// 5. 先构建校区节点
// 4. 先构建校区节点
for (XgDormCampus campus : campusList) {
BuildingTreeSelectVo campusNode = new BuildingTreeSelectVo();
campusNode.setId(campus.getId());
campusNode.setLabel(campus.getCampusName());
campusNode.setNodeType("campus");
campusNode.setDisabled(true); // 校区不可选
campusNode.setChildren(new ArrayList<>());
// 设置创建者和更新者字段
campusNode.setCreateBy(campus.getCreateBy());
campusNode.setCreateTime(campus.getCreateTime());
campusNode.setUpdateBy(campus.getUpdateBy());
campusNode.setUpdateTime(campus.getUpdateTime());
// 添加该校区下的宿舍区
List<XgDormArea> campusAreas = areaList.stream()
.filter(area -> area.getCampusId().equals(campus.getId()))
// 检查该校区下是否有宿舍楼
List<XgDormBuilding> campusBuildings = buildingList.stream()
.filter(building -> {
// 通过宿舍区找到校区
XgDormArea area = areaMapper.selectById(building.getAreaId());
return area != null && area.getCampusId().equals(campus.getId());
})
.collect(Collectors.toList());
for (XgDormArea area : campusAreas) {
BuildingTreeSelectVo areaNode = new BuildingTreeSelectVo();
areaNode.setId(area.getId());
areaNode.setLabel(area.getAreaName());
areaNode.setNodeType("area");
areaNode.setDisabled(true); // 宿舍区不可选
areaNode.setChildren(new ArrayList<>());
if (!campusBuildings.isEmpty()) {
BuildingTreeSelectVo campusNode = new BuildingTreeSelectVo();
campusNode.setId(campus.getId());
campusNode.setLabel(campus.getCampusName());
campusNode.setNodeType("campus");
campusNode.setDisabled(true); // 校区不可选
campusNode.setChildren(new ArrayList<>());
// 设置创建者和更新者字段
areaNode.setCreateBy(area.getCreateBy());
areaNode.setCreateTime(area.getCreateTime());
areaNode.setUpdateBy(area.getUpdateBy());
areaNode.setUpdateTime(area.getUpdateTime());
campusNode.setCreateBy(campus.getCreateBy());
campusNode.setCreateTime(campus.getCreateTime());
campusNode.setUpdateBy(campus.getUpdateBy());
campusNode.setUpdateTime(campus.getUpdateTime());
// 添加该宿舍区下的宿舍楼
List<XgDormBuilding> areaBuildings = buildingList.stream()
.filter(building -> building.getAreaId().equals(area.getId()))
.collect(Collectors.toList());
for (XgDormBuilding building : areaBuildings) {
// 直接添加该校区下的宿舍楼,跳过宿舍区层级
for (XgDormBuilding building : campusBuildings) {
BuildingTreeSelectVo buildingNode = new BuildingTreeSelectVo();
buildingNode.setId(building.getId());
buildingNode.setLabel(building.getBuildingName());
@@ -222,15 +205,57 @@ public class XgDormBuildingServiceImpl implements IXgDormBuildingService {
buildingNode.setUpdateBy(building.getUpdateBy());
buildingNode.setUpdateTime(building.getUpdateTime());
areaNode.getChildren().add(buildingNode);
campusNode.getChildren().add(buildingNode);
}
campusNode.getChildren().add(areaNode);
treeList.add(campusNode);
}
treeList.add(campusNode);
}
return treeList;
}
/**
* 分页查询宿舍楼信息简化列表
* 用于简化界面显示,隐藏宿舍区层级,直接显示校区→宿舍楼结构
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 宿舍楼信息简化分页列表
*/
@Override
public TableDataInfo<XgDormBuildingSimplifiedVo> querySimplifiedPageList(XgDormBuildingBo bo, PageQuery pageQuery) {
// 先查询完整的分页数据
TableDataInfo<XgDormBuildingVo> fullPageList = queryPageList(bo, pageQuery);
// 转换为简化版VO
List<XgDormBuildingSimplifiedVo> simplifiedList = fullPageList.getRows().stream()
.map(XgDormBuildingSimplifiedVo::fromFullVo)
.collect(Collectors.toList());
// 创建简化版分页结果
TableDataInfo<XgDormBuildingSimplifiedVo> simplifiedPageList = new TableDataInfo<>();
simplifiedPageList.setRows(simplifiedList);
simplifiedPageList.setTotal(fullPageList.getTotal());
return simplifiedPageList;
}
/**
* 查询符合条件的宿舍楼信息简化列表
* 用于简化界面显示,隐藏宿舍区层级,直接显示校区→宿舍楼结构
*
* @param bo 查询条件
* @return 宿舍楼信息简化列表
*/
@Override
public List<XgDormBuildingSimplifiedVo> querySimplifiedList(XgDormBuildingBo bo) {
// 先查询完整的数据
List<XgDormBuildingVo> fullList = queryList(bo);
// 转换为简化版VO
return fullList.stream()
.map(XgDormBuildingSimplifiedVo::fromFullVo)
.collect(Collectors.toList());
}
}
@@ -8,6 +8,7 @@ import lombok.RequiredArgsConstructor;
import org.dromara.xg.service.IXgDormBuildingTreeService;
import org.dromara.xg.mapper.XgDormBuildingTreeMapper;
import org.dromara.xg.domain.vo.DormBuildingTreeVo;
import org.dromara.xg.domain.vo.DormBuildingTreeSimplifiedVo;
import org.dromara.xg.domain.vo.DormBuildingInfoVo;
import org.dromara.xg.domain.bo.DormBuildingTreeBo;
import org.dromara.common.satoken.utils.LoginHelper;
@@ -231,4 +232,42 @@ public class XgDormBuildingTreeServiceImpl implements IXgDormBuildingTreeService
return new ArrayList<>(campusMap.values());
}
/**
* 获取宿舍楼树形结构简化数据
* 校区-宿舍楼二级结构,隐藏宿舍区层级
*
* @param bo 查询参数
* @return 简化树形结构数据
*/
@Override
public List<DormBuildingTreeSimplifiedVo> getSimplifiedDormBuildingTree(DormBuildingTreeBo bo) {
// 先获取完整的树形结构数据
List<DormBuildingTreeVo> fullTreeData = getDormBuildingTree(bo);
// 转换为简化版树形结构
return fullTreeData.stream()
.map(DormBuildingTreeSimplifiedVo::fromFullTreeVo)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
/**
* 获取所有宿舍楼树形结构简化数据(不考虑权限)
* 校区-宿舍楼二级结构,隐藏宿舍区层级
*
* @param bo 查询参数
* @return 简化树形结构数据
*/
@Override
public List<DormBuildingTreeSimplifiedVo> getAllSimplifiedDormBuildingTree(DormBuildingTreeBo bo) {
// 先获取完整的树形结构数据(不考虑权限)
List<DormBuildingTreeVo> fullTreeData = getAllDormBuildingTree(bo);
// 转换为简化版树形结构
return fullTreeData.stream()
.map(DormBuildingTreeSimplifiedVo::fromFullTreeVo)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
}
@@ -144,8 +144,8 @@ public class XgDormRoomServiceImpl implements IXgDormRoomService {
private LambdaQueryWrapper<XgDormRoom> buildQueryWrapper(XgDormRoomBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<XgDormRoom> lqw = Wrappers.lambdaQuery();
lqw.orderByAsc(XgDormRoom::getSort); // 第一优先级:按排序号升序
lqw.orderByAsc(XgDormRoom::getFloorNum); // 第二优先级:按楼层升序
lqw.orderByAsc(XgDormRoom::getFloorNum); // 第一优先级:按楼层升序
lqw.orderByAsc(XgDormRoom::getSort); // 第二优先级:按排序号升序
lqw.orderByAsc(XgDormRoom::getRoomName); // 第三优先级:按房间名称升序
lqw.like(StringUtils.isNotBlank(bo.getRoomCode()), XgDormRoom::getRoomCode, bo.getRoomCode());
lqw.like(StringUtils.isNotBlank(bo.getRoomName()), XgDormRoom::getRoomName, bo.getRoomName());
@@ -333,4 +333,83 @@ public interface DormHygieneStudentScoreMapper extends BaseMapperPlus<DormHygien
**文档版本:** v1.0
**创建时间:** 2025-01-28
**维护人员:** 系统开发团队
**维护人员:** 系统开发团队
---
## 修复记录
### 2025-01-29 界面问题修复
#### 修复问题一:新增时个人扣分默认值显示
**问题现象:** 点击新增按钮时,弹窗中房间学生信息列表的个人扣分列显示默认0分placeholder,而不是直接填入0。
**解决方案:** 修改`DormHygieneCheckDialog.vue`中`loadRoomStudents`方法,将新增模式下学生的`student_score`默认值从`null`改为`0`。
```javascript
// 修改前
student_score: student.student_score || null, // 个人得分,默认为空
// 修改后
student_score: 0, // 个人得分,新增时默认为0
```
#### 修复问题二:宿舍扣分变为0时个人扣分不同步更新
**问题现象:** 当宿舍扣分从1改成0时,房间学生信息列表中个人扣分还是1,没有变成0。
**解决方案:** 修改`handleTotalScoreChange`方法,添加对0分的特别处理逻辑。
```javascript
// 修改后的逻辑
const handleTotalScoreChange = () => {
// 验证宿舍总分范围
if (form.totalScore !== null && form.totalScore !== undefined && form.totalScore !== '') {
// 处理非0分情况
const score = parseFloat(form.totalScore.toString());
if (isNaN(score) || score < 0 || score > 100) {
ElMessage.warning('宿舍总分必须在0-100分之间');
form.totalScore = 0;
return;
}
// 自动为所有学生设置相同的个人得分(包括0分)
if (roomStudents.value.length > 0) {
roomStudents.value.forEach((student: any) => {
student.student_score = score;
});
ElMessage.success(`已自动为${roomStudents.value.length}名学生设置个人得分:${score}分`);
}
} else if (form.totalScore === 0 || form.totalScore === '0') {
// 特别处理0分的情况
if (roomStudents.value.length > 0) {
roomStudents.value.forEach((student: any) => {
student.student_score = 0;
});
ElMessage.success(`已自动为${roomStudents.value.length}名学生设置个人得分:0分`);
}
}
};
```
#### 修复问题三:房间排序问题
**问题现象:** 选择宿舍楼名称后,房间名称按照sort序号进行排序返回,但因为每层房间的sort都从0开始编码,导致返回数据把每个楼层的第一个房间先返回。
**解决方案:** 修改后端`XgDormRoomServiceImpl.java`中的`buildQueryWrapper`方法,调整排序逻辑为先按楼层再按sort排序。
```java
// 修改前
lqw.orderByAsc(XgDormRoom::getSort); // 第一优先级:按排序号升序
lqw.orderByAsc(XgDormRoom::getFloorNum); // 第二优先级:按楼层升序
// 修改后
lqw.orderByAsc(XgDormRoom::getFloorNum); // 第一优先级:按楼层升序
lqw.orderByAsc(XgDormRoom::getSort); // 第二优先级:按排序号升序
```
#### 修复效果
1. **问题一**:新增时个人扣分直接显示0,用户体验更好
2. **问题二**:宿舍扣分与个人扣分完全同步,包括0分的情况
3. **问题三**:房间列表按楼层顺序显示,每层内部按sort排序,符合用户期望
#### 涉及文件
- `web/src/views/xg/xgss/dormHygieneCheck/dormHygieneCheck/DormHygieneCheckDialog.vue`
- `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRoomServiceImpl.java`
+135 -41
View File
@@ -2,11 +2,13 @@
## 接口说明
本文档描述了获取宿舍楼树形结构数据的API接口,该接口返回校区-宿舍区-宿舍楼的三级树形结构,其中校区和宿舍区节点不可选,只有宿舍楼节点可以选择。
本文档描述了获取宿舍楼树形结构数据的API接口。**重要变更**:根据业务需求调整,主要接口已从 "校区-宿舍区-宿舍楼" 三级结构改为 "校区-宿舍楼" 二级结构,隐藏宿舍区层级,简化用户操作。
## 接口定义
### 获取宿舍楼树形结构
### 🔄 主要接口(二级结构)
#### 1. 获取宿舍楼树形结构(推荐使用)
**请求URL**:`/sslxx/dormBuilding/buildingTree`
@@ -14,6 +16,8 @@
**权限标识**:`sslxx:dormBuilding:list`
**结构说明**:校区-宿舍楼二级结构,校区不可选,只有宿舍楼可以选择
**请求参数**:无
**响应示例**:
@@ -30,24 +34,16 @@
"disabled": true,
"children": [
{
"id": 101,
"label": "东区宿舍区",
"nodeType": "area",
"disabled": true,
"children": [
{
"id": 1001,
"label": "1号楼",
"nodeType": "building",
"disabled": false
},
{
"id": 1002,
"label": "2号楼",
"nodeType": "building",
"disabled": false
}
]
"id": 1001,
"label": "1号楼",
"nodeType": "building",
"disabled": false
},
{
"id": 1002,
"label": "2号楼",
"nodeType": "building",
"disabled": false
}
]
},
@@ -58,22 +54,92 @@
"disabled": true,
"children": [
{
"id": 201,
"label": "西区宿舍区",
"nodeType": "area",
"id": 2001,
"label": "3号楼",
"nodeType": "building",
"disabled": false
},
{
"id": 2002,
"label": "4号楼",
"nodeType": "building",
"disabled": false
}
]
}
]
}
```
#### 2. 获取宿舍楼树形结构(基于权限)
**请求URL**:`/xgss/dormBuildingTree/list`
**请求方式**:`GET`
**权限标识**:`xgss:dormBuildingTree:list`
**结构说明**:校区-宿舍楼二级结构,根据当前用户权限过滤
**请求参数**:
| 参数名 | 类型 | 必填 | 描述 |
|-----------|--------|------|----------------|
| campusId | Long | 否 | 校区ID过滤条件 |
| buildingName | String | 否 | 宿舍楼名称过滤条件 |
**响应格式**:与上述接口相同
#### 3. 获取所有宿舍楼树形结构(不考虑权限)
**请求URL**:`/xgss/dormBuildingTree/listAll`
**请求方式**:`GET`
**权限标识**:`xgss:dormBuildingTree:list`
**结构说明**:校区-宿舍楼二级结构,返回所有数据,不进行权限过滤
**响应格式**:与上述接口相同
### 📋 特殊用途接口(三级结构)
#### 4. 获取完整宿舍楼树形结构(包含宿舍区)
**请求URL**:`/xgss/dormBuildingTree/fullList`
**请求方式**:`GET`
**权限标识**:`xgss:dormBuildingTree:list`
**结构说明**:校区-宿舍区-宿舍楼三级结构,仅用于特殊需求
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{
"id": 1,
"label": "东校区",
"type": "campus",
"disabled": true,
"children": [
{
"id": 101,
"label": "东区宿舍区",
"type": "area",
"disabled": true,
"children": [
{
"id": 2001,
"label": "3号楼",
"nodeType": "building",
"disabled": false
},
{
"id": 2002,
"label": "4号楼",
"nodeType": "building",
"disabled": false
"id": 1001,
"label": "1号楼",
"type": "building",
"disabled": false,
"buildingFloors": 6,
"buildingType": "1"
}
]
}
@@ -85,7 +151,7 @@
## 数据结构
### BuildingTreeSelectVo
### BuildingTreeSelectVo(二级结构)
树形结构节点对象:
@@ -93,7 +159,7 @@
|------------|-------------------|----------------------------------------|
| id | Long | 节点ID |
| label | String | 节点名称 |
| nodeType | String | 节点类型:campus(校区)、area(宿舍区)、building(宿舍楼) |
| nodeType | String | 节点类型:campus(校区)、building(宿舍楼) |
| disabled | Boolean | 是否禁用选择 |
| children | List<BuildingTreeSelectVo> | 子节点列表 |
| createBy | Long | 创建者 |
@@ -101,6 +167,20 @@
| updateBy | Long | 更新者 |
| updateTime | Date | 更新时间 |
### DormBuildingTreeVo(完整结构)
完整三级结构节点对象:
| 字段名 | 类型 | 描述 |
|-----------------|-------------------|----------------------------------------|
| id | Long | 节点ID |
| label | String | 节点名称 |
| type | String | 节点类型:campus(校区)、area(宿舍区)、building(宿舍楼) |
| disabled | Boolean | 是否禁用选择 |
| children | List<DormBuildingTreeVo> | 子节点列表 |
| buildingFloors | Integer | 宿舍楼层数(仅宿舍楼节点) |
| buildingType | String | 宿舍楼类型(仅宿舍楼节点) |
## 使用方法
### 前端使用示例
@@ -114,7 +194,7 @@ import type { BuildingTreeSelectVo } from '@/api/sslxx/dormBuilding/types';
// 在组件的setup函数中
const treeData = ref<BuildingTreeSelectVo[]>([]);
// 加载宿舍楼树形数据
// 加载宿舍楼树形数据(推荐使用二级结构)
const loadBuildingTree = async () => {
try {
const res = await getBuildingTree();
@@ -155,20 +235,34 @@ loadBuildingTree();
## 特殊说明
1. 接口返回的树形结构中,校区和宿舍区节点已设置 `disabled=true`,前端无需额外处理即可实现只能选择宿舍楼节点的效果
2. 使用 `check-strictly=true` 可以确保树形选择器的选中状态不会级联影响父子节点
3. 该接口已支持管理员分配宿舍楼管理范围的业务需求
### ⚠️ 重要变更说明
1. **主要接口结构调整**:为简化用户操作,去除宿舍区概念,主要接口已改为二级结构
2. **向后兼容**:保留了 `fullList` 接口供需要完整三级结构的特殊场景使用
3. **业务影响**:前端界面将不再显示宿舍区层级,用户直接在校区下选择宿舍楼
### 📋 使用建议
1. **推荐使用二级结构接口**:适用于绝大多数业务场景
2. **校区节点设置为不可选**:确保用户只能选择宿舍楼
3. **使用权限过滤接口**:根据用户权限显示相应的宿舍楼范围
## 相关API接口
- [宿舍管理员授权API](./宿舍管理员授权API.md)
- [宿舍床位分配管理API](./宿舍床位分配管理API.md)
## 数据库关系
- `xg_dorm_building` 表通过 `campus_id` 字段关联 `xg_dorm_campus` 表
- `xg_dorm_building` 表通过 `area_id` 字段关联 `xg_dorm_area` 表
- `xg_dorm_building` 表通过 `area_id` 字段关联 `xg_dorm_area` 表(数据库保留,UI隐藏)
- `xg_dorm_area` 表通过 `campus_id` 字段关联 `xg_dorm_campus` 表
## 版本历史
- **2025-01-29**: 接口结构重大调整,主要接口从三级改为二级结构
- **2025-07-20**: 初始版本,三级结构
## 最后更新时间
2025-07-20
2025-01-29
+2 -2
View File
@@ -4376,7 +4376,7 @@ CREATE TABLE `xg_dorm_hygiene_check` (
`area_name` varchar(100) DEFAULT NULL COMMENT '宿舍区名称',
`campus_id` bigint(20) DEFAULT NULL COMMENT '校区ID,关联xg_dorm_campus.id',
`campus_name` varchar(100) DEFAULT NULL COMMENT '校区名称',
`total_score` decimal(5,2) DEFAULT NULL COMMENT '宿舍总分(0-100分)',
`total_score` decimal(5,2) DEFAULT NULL COMMENT '宿舍扣分(0-100分)',
`hygiene_grade` char(1) DEFAULT NULL COMMENT '卫生等级(A优秀 B良好 C合格 D不合格)',
`attachment_ids` text COMMENT '附件OSS ID列表,逗号分隔',
`check_summary` text COMMENT '检查总结',
@@ -4422,7 +4422,7 @@ CREATE TABLE `xg_dorm_hygiene_student_score` (
`college_name` varchar(100) DEFAULT NULL COMMENT '学院名称',
`bed_id` bigint(20) DEFAULT NULL COMMENT '床位ID,关联xg_dorm_bed.id',
`bed_code` varchar(50) DEFAULT NULL COMMENT '床位编号',
`student_score` decimal(5,2) DEFAULT NULL COMMENT '学生个人得分(0-100分)',
`student_score` decimal(5,2) DEFAULT NULL COMMENT '学生个人扣分(0-100分)',
`attendance_status` char(1) DEFAULT '1' COMMENT '出勤状态(1在寝 2外出 3请假 4其他)',
`student_grade` char(1) DEFAULT NULL COMMENT '学生评级(A优秀 B良好 C合格 D不合格)',
`is_responsible_person` tinyint(1) DEFAULT '0' COMMENT '是否为寝室长(0否 1是)',
@@ -1,7 +1,7 @@
<template>
<el-dialog :title="dialogTitle" v-model="dialogVisible" width="1200px" append-to-body @close="handleClose">
<el-form ref="dormHygieneCheckFormRef" :model="form" :rules="rules" label-width="120px">
<!-- 第一行:检查标题、校区名称、宿舍区名称 -->
<!-- 第一行:检查标题、校区名称、宿舍楼名称 -->
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="检查标题" prop="checkTitle">
@@ -23,26 +23,6 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="宿舍区名称" prop="areaId">
<el-select
v-model="form.areaId"
placeholder="请选择宿舍区"
clearable
filterable
:loading="dormLoading"
@change="handleAreaChange"
style="width: 100%"
:disabled="!form.campusId"
>
<el-option v-for="area in areaOptions" :key="area.id" :label="area.areaName" :value="area.id" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行:宿舍楼名称、房间名称、宿舍总分 -->
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="宿舍楼名称" prop="buildingId">
<el-select
@@ -53,12 +33,16 @@
:loading="dormLoading"
@change="handleBuildingChange"
style="width: 100%"
:disabled="!form.areaId"
:disabled="!form.campusId"
>
<el-option v-for="building in buildingOptions" :key="building.id" :label="building.buildingName" :value="building.id" />
<el-option v-for="building in buildingOptions" :key="building.id" :label="getBuildingDisplayName(building)" :value="building.id" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行:房间名称、宿舍扣分、卫生等级 -->
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="房间名称" prop="roomId">
<el-select
@@ -76,10 +60,10 @@
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="宿舍总分" prop="totalScore">
<el-form-item label="宿舍扣分" prop="totalScore">
<el-input
v-model="form.totalScore"
placeholder="请输入宿舍总分(0-100分)"
placeholder="请输入宿舍扣分(默认0分)"
type="number"
:min="0"
:max="100"
@@ -88,10 +72,6 @@
/>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行:卫生等级、检查类型、检查级别 -->
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="卫生等级" prop="hygieneGrade">
<el-select v-model="form.hygieneGrade" placeholder="请选择卫生等级" style="width: 100%" @change="handleHygieneGradeChange">
@@ -99,6 +79,10 @@
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行:检查类型、检查级别、检查员姓名 -->
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="检查类型" prop="checkType">
<el-select v-model="form.checkType" placeholder="请选择检查类型" style="width: 100%">
@@ -113,10 +97,6 @@
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 第四行:检查员姓名、检查日期 -->
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="检查员姓名" prop="checkerName">
<el-select
@@ -140,6 +120,10 @@
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 第四行:检查日期 -->
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="检查日期" prop="checkDate">
<el-date-picker
@@ -167,12 +151,12 @@
<div class="room-students-container">
<div class="room-students-table-wrapper">
<el-table :data="roomStudents" stripe size="small" table-layout="auto">
<!-- 学生个人得分 -->
<el-table-column label="个人得分" width="120" fixed="left">
<!-- 学生个人扣分 -->
<el-table-column label="个人扣分" width="120" fixed="left">
<template #default="scope">
<el-input
v-model="scope.row.student_score"
placeholder="0-100分"
placeholder="默认0分"
size="small"
type="number"
:min="0"
@@ -415,14 +399,10 @@ interface CampusOptionVO {
campusName: string;
}
interface AreaOptionVO {
id: string | number;
areaName: string;
}
interface BuildingOptionVO {
id: string | number;
buildingName: string;
buildingType?: string; // 宿舍楼类型(1男生楼 2女生楼 3混合楼)
}
interface RoomOptionVO {
@@ -457,7 +437,6 @@ const dormHygieneCheckFormRef = ref();
// 宿舍级联选择器相关状态
const campusOptions = ref<CampusOptionVO[]>([]);
const areaOptions = ref<AreaOptionVO[]>([]);
const buildingOptions = ref<BuildingOptionVO[]>([]);
const roomOptions = ref<RoomOptionVO[]>([]);
const dormLoading = ref(false);
@@ -517,11 +496,9 @@ const initFormData: DormHygieneCheckForm = {
roomName: undefined,
buildingId: undefined,
buildingName: undefined,
areaId: undefined,
areaName: undefined,
campusId: undefined,
campusName: undefined,
totalScore: undefined,
totalScore: 0,
hygieneGrade: 'A', // 默认 A - 优秀
attachmentIds: undefined,
checkSummary: undefined,
@@ -722,8 +699,7 @@ const resetForm = () => {
attachmentList.value = [];
roomStudents.value = [];
// 清空宿舍级联选项
areaOptions.value = [];
// 清空宿舍级联选项 - 移除宿舍区相关
buildingOptions.value = [];
roomOptions.value = [];
@@ -812,15 +788,12 @@ const loadCampusOptions = async () => {
/** 校区选择变化处理 */
const handleCampusChange = async (campusId: string | number) => {
console.log('校区选择变化处理', campusId);
// 清空下级选项
form.areaId = undefined;
form.areaName = undefined;
// 清空下级选项 - 移除宿舍区相关字段
form.buildingId = undefined;
form.buildingName = undefined;
form.roomId = undefined;
form.roomName = undefined;
areaOptions.value = [];
buildingOptions.value = [];
roomOptions.value = [];
@@ -832,77 +805,22 @@ const handleCampusChange = async (campusId: string | number) => {
form.campusName = selectedCampus.campusName;
}
// 加载宿舍区选项
await loadAreaOptions(campusId);
// 直接加载宿舍楼选项(根据校区过滤)
await loadBuildingOptions(campusId);
} else {
form.campusId = undefined;
form.campusName = undefined;
}
};
/** 加载宿舍区选项 */
const loadAreaOptions = async (campusId: string | number) => {
try {
const cacheKey = `area_${campusId}`;
const cachedData = getCachedData(cacheKey);
if (cachedData) {
areaOptions.value = cachedData;
return;
}
dormLoading.value = true;
const res = await request({
url: '/ssqxx/dormArea/list',
method: 'get',
params: { campusId }
});
const data = res.rows || [];
areaOptions.value = data;
setCachedData(cacheKey, data);
} catch (error) {
console.error('加载宿舍区选项失败:', error);
areaOptions.value = [];
} finally {
dormLoading.value = false;
}
};
/** 宿舍区选择变化处理 */
const handleAreaChange = async (areaId: string | number) => {
// 清空下级选项
form.buildingId = undefined;
form.buildingName = undefined;
form.roomId = undefined;
form.roomName = undefined;
buildingOptions.value = [];
roomOptions.value = [];
if (areaId) {
// 设置宿舍区名称
const selectedArea = areaOptions.value.find((area) => area.id === areaId);
if (selectedArea) {
form.areaId = areaId;
form.areaName = selectedArea.areaName;
}
// 加载宿舍楼选项
await loadBuildingOptions(areaId);
} else {
form.areaId = undefined;
form.areaName = undefined;
}
};
/** 加载宿舍楼选项 */
const loadBuildingOptions = async (areaId: string | number) => {
/** 加载宿舍楼选项 - 修改为直接根据校区ID过滤 */
const loadBuildingOptions = async (campusId: string | number) => {
try {
dormLoading.value = true;
const res = await request({
url: '/sslxx/dormBuilding/list',
method: 'get',
params: { areaId }
params: { campusId } // 直接使用校区ID过滤,不再使用areaId
});
buildingOptions.value = res.rows || [];
} catch (error) {
@@ -1075,18 +993,13 @@ const loadCascadeOptionsForUpdate = async (formData: DormHygieneCheckForm) => {
// 并行加载所有需要的宿舍选项,提升性能
const loadTasks = [];
// 如果有校区ID,加载宿舍区选项
// 如果有校区ID,加载宿舍楼选项
if (formData.campusId) {
loadTasks.push(loadAreaOptions(formData.campusId));
loadTasks.push(loadBuildingOptions(formData.campusId));
// 如果有宿舍区ID,加载宿舍楼选项
if (formData.areaId) {
loadTasks.push(loadBuildingOptions(formData.areaId));
// 如果有宿舍楼ID,加载房间选项
if (formData.buildingId) {
loadTasks.push(loadRoomOptions(formData.buildingId));
}
// 如果有宿舍楼ID,加载房间选项
if (formData.buildingId) {
loadTasks.push(loadRoomOptions(formData.buildingId));
}
}
@@ -1163,7 +1076,7 @@ const loadRoomStudents = async () => {
// 为每个学生设置默认值
roomStudents.value = res.data.map((student: any) => ({
...student,
student_score: student.student_score || null, // 个人得分,默认为空
student_score: 0, // 个人得分,新增时默认为0
attendance_status: student.attendance_status || '1', // 出勤状态,默认1-在寝
student_grade: student.student_grade || 'A' // 学生评级,默认A-优秀
}));
@@ -1414,6 +1327,20 @@ const handleAttachmentData = async () => {
}
};
// 宿舍楼选项显示格式
const getBuildingDisplayName = (building: BuildingOptionVO) => {
let displayName = building.buildingName;
if (building.buildingType) {
if (building.buildingType === '1') {
displayName = `(男)${displayName}`;
} else if (building.buildingType === '2') {
displayName = `(女)${displayName}`;
}
// 混合楼(3)不显示性别标识
}
return displayName;
};
// 组件挂载时加载校区选项
const initCampusOptions = async () => {
await loadCampusOptions();
@@ -1482,21 +1409,29 @@ const handleStudentGradeChange = (student: any) => {
// 处理宿舍总分变化 - 自动填写学生个人得分
const handleTotalScoreChange = () => {
// 验证宿舍总分范围
if (form.totalScore !== null && form.totalScore !== undefined && form.totalScore !== 0 && String(form.totalScore) !== '0') {
if (form.totalScore !== null && form.totalScore !== undefined && form.totalScore !== '') {
const score = parseFloat(form.totalScore.toString());
if (isNaN(score) || score < 0 || score > 100) {
ElMessage.warning('宿舍总分必须在0-100分之间');
form.totalScore = null;
form.totalScore = 0;
return;
}
// 如果分数有效且有学生,自动为所有学生设置相同的个人得分
// 如果分数有效且有学生,自动为所有学生设置相同的个人得分(包括0分)
if (roomStudents.value.length > 0) {
roomStudents.value.forEach((student: any) => {
student.student_score = score;
});
ElMessage.success(`已自动为${roomStudents.value.length}名学生设置个人得分:${score}分`);
}
} else if (form.totalScore === 0 || form.totalScore === '0') {
// 特别处理0分的情况
if (roomStudents.value.length > 0) {
roomStudents.value.forEach((student: any) => {
student.student_score = 0;
});
ElMessage.success(`已自动为${roomStudents.value.length}名学生设置个人得分:0分`);
}
}
};
@@ -106,9 +106,8 @@
</el-table-column>
<el-table-column label="房间名称" align="center" prop="roomName" />
<el-table-column label="宿舍楼名称" align="center" prop="buildingName" />
<el-table-column label="宿舍区名称" align="center" prop="areaName" />
<el-table-column label="校区名称" align="center" prop="campusName" />
<el-table-column label="宿舍总分" align="center" prop="totalScore" />
<el-table-column label="宿舍扣分" align="center" prop="totalScore" />
<el-table-column label="卫生等级" align="center" prop="hygieneGrade">
<template #default="scope">
<dict-tag :options="xg_dorm_hygiene_grade" :value="scope.row.hygieneGrade" />
@@ -138,16 +137,17 @@
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
<pagination v-show="total > 0" :total="total" :page="queryParams.pageNum" :limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 宿舍卫生检查弹窗组件 -->
<DormHygieneCheckDialog
v-model="dialog.visible"
:visible="dialog.visible"
:title="dialog.title"
:form-data="form"
@submit="handleDialogSubmit"
@close="handleDialogClose"
@update:visible="(value) => (dialog.visible = value)"
/>
</div>
</template>
@@ -164,7 +164,7 @@ import {
addDormHygieneCheck,
updateDormHygieneCheck,
exportDormHygieneCheck
} from '@/api/xg/xgss/dormHygieneCheck/dormHygieneCheck';
} from '@/api/xg/xgss/dormHygieneCheck/dormHygieneCheck/index';
import {
DormHygieneCheckVO,
DormHygieneCheckQuery,
@@ -223,7 +223,7 @@ const initFormData: DormHygieneCheckForm = {
areaName: undefined,
campusId: undefined,
campusName: undefined,
totalScore: undefined,
totalScore: 0,
hygieneGrade: 'A',
attachmentIds: undefined,
checkSummary: undefined,
@@ -17,24 +17,11 @@
</el-col>
<el-col :span="6">
<el-form-item label="所属校区" prop="campusId">
<el-select v-model="queryParams.campusId" placeholder="请选择所属校区" clearable @change="handleCampusChange">
<el-select v-model="queryParams.campusId" placeholder="请选择所属校区" clearable>
<el-option v-for="item in campusList" :key="item.id" :label="item.campusName" :value="item.id" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="所属宿舍区" prop="areaId">
<el-tree-select
v-model="queryParams.areaId"
:data="areaTreeData"
:props="{ label: 'areaName', value: 'id' }"
placeholder="请选择所属宿舍区"
clearable
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="宿舍楼类型" prop="buildingType">
<el-select v-model="queryParams.buildingType" placeholder="请选择宿舍楼类型" clearable>
@@ -72,9 +59,6 @@
>删除</el-button
>
</el-col>
<!-- <el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['sslxx:dormBuilding:export']">导出</el-button>
</el-col> -->
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
@@ -85,7 +69,6 @@
<el-table-column label="宿舍楼编码" align="center" prop="buildingCode" />
<el-table-column label="宿舍楼名称" align="center" prop="buildingName" />
<el-table-column label="所属校区" align="center" prop="campusName" />
<el-table-column label="所属宿舍区" align="center" prop="areaName" />
<el-table-column label="总楼层数" align="center" prop="floors" />
<el-table-column label="宿舍楼类型" align="center" prop="buildingType">
<template #default="scope">
@@ -112,19 +95,10 @@
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="dormBuildingFormRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="所属校区" prop="campusId">
<el-select v-model="form.campusId" placeholder="请选择所属校区" clearable @change="handleFormCampusChange">
<el-select v-model="form.campusId" placeholder="请选择所属校区" clearable>
<el-option v-for="item in campusList" :key="item.id" :label="item.campusName" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="所属宿舍区" prop="areaId">
<el-tree-select
v-model="form.areaId"
:data="formAreaTreeData"
:props="{ label: 'areaName', value: 'id' }"
placeholder="请选择所属宿舍区"
clearable
/>
</el-form-item>
<el-form-item label="宿舍楼编码" prop="buildingCode">
<el-input v-model="form.buildingCode" placeholder="请输入宿舍楼编码" />
</el-form-item>
@@ -171,13 +145,12 @@ import {
delDormBuilding,
addDormBuilding,
updateDormBuilding,
listCampusOptions,
listAreaTreeData
listCampusOptions
} from '@/api/xg/xgss/sslxx/dormBuilding';
import { DormBuildingVO, DormBuildingQuery, DormBuildingForm } from '@/api/xg/xgss/sslxx/dormBuilding/types';
import { DormCampusVO } from '@/api/xg/xgss/xqxx/dormCampus/types';
import { DormAreaVO } from '@/api/xg/xgss/ssqxx/dormArea/types';
import { getCurrentInstance, ComponentInternalInstance, ref, reactive, toRefs, onMounted } from 'vue';
import { ElForm, ElMessageBox, ElMessage } from 'element-plus';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { dorm_type } = toRefs<any>(proxy?.useDict('dorm_type'));
@@ -191,11 +164,9 @@ const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const campusList = ref<DormCampusVO[]>([]); // 校区列表
const areaTreeData = ref<any[]>([]); // 宿舍区树形数据
const formAreaTreeData = ref<any[]>([]); // 表单宿舍区树形数据
const queryFormRef = ref<ElFormInstance>();
const dormBuildingFormRef = ref<ElFormInstance>();
const queryFormRef = ref<InstanceType<typeof ElForm>>();
const dormBuildingFormRef = ref<InstanceType<typeof ElForm>>();
const dialog = reactive<DialogOption>({
visible: false,
@@ -206,7 +177,6 @@ const initFormData: DormBuildingForm = {
id: undefined,
buildingCode: undefined,
buildingName: undefined,
areaId: undefined,
campusId: undefined,
buildingType: undefined,
buildingDescription: undefined,
@@ -220,7 +190,6 @@ const data = reactive<PageData<DormBuildingForm, DormBuildingQuery>>({
pageSize: 10,
buildingCode: undefined,
buildingName: undefined,
areaId: undefined,
campusId: undefined,
buildingType: undefined,
sortOrder: undefined,
@@ -230,7 +199,6 @@ const data = reactive<PageData<DormBuildingForm, DormBuildingQuery>>({
id: [{ required: true, message: '主键ID不能为空', trigger: 'blur' }],
buildingCode: [{ required: true, message: '宿舍楼编码不能为空', trigger: 'blur' }],
buildingName: [{ required: true, message: '宿舍楼名称不能为空', trigger: 'blur' }],
areaId: [{ required: true, message: '所属宿舍区ID不能为空', trigger: 'blur' }],
campusId: [{ required: true, message: '所属校区ID不能为空', trigger: 'blur' }]
}
});
@@ -252,102 +220,6 @@ const loadCampusOptions = async () => {
campusList.value = res.rows || [];
};
/** 加载宿舍区树形数据 */
const loadAreaTreeData = async (campusId?: string | number) => {
const res = await listAreaTreeData(campusId);
if (campusId) {
// 如果指定了校区ID,只展示该校区的宿舍区
areaTreeData.value = res.rows || [];
formAreaTreeData.value = res.rows || []; // 同时更新表单树形数据
} else {
// 如果没有指定校区ID,按照校区分组展示宿舍区
const campusMap = new Map();
// 先将校区列表转为Map
campusList.value.forEach((campus) => {
campusMap.set(campus.id, {
id: campus.id,
areaName: campus.campusName,
children: []
});
});
// 将宿舍区按校区分组
const areas = res.rows || [];
areas.forEach((area: DormAreaVO) => {
if (campusMap.has(area.campusId)) {
campusMap.get(area.campusId).children.push({
id: area.id,
areaName: area.areaName
});
}
});
// 转换为树形结构
areaTreeData.value = Array.from(campusMap.values());
formAreaTreeData.value = Array.from(campusMap.values());
}
};
/** 校区选择变化处理函数 */
const handleCampusChange = (value: string | number) => {
queryParams.value.areaId = undefined; // 清空已选择的宿舍区
loadAreaTreeData(value);
};
/** 表单校区选择变化处理函数 */
const handleFormCampusChange = (value: string | number) => {
form.value.areaId = undefined; // 清空已选择的宿舍区
loadFormAreaTreeData(value);
};
/** 加载表单宿舍区树形数据 */
const loadFormAreaTreeData = async (campusId?: string | number) => {
const res = await listAreaTreeData(campusId);
if (campusId) {
// 如果指定了校区ID,只展示该校区的宿舍区
formAreaTreeData.value = res.rows || [];
} else {
// 如果没有指定校区ID,按照校区分组展示宿舍区
const campusMap = new Map();
// 先将校区列表转为Map
campusList.value.forEach((campus) => {
campusMap.set(campus.id, {
id: campus.id,
areaName: campus.campusName,
children: []
});
});
// 将宿舍区按校区分组
const areas = res.rows || [];
areas.forEach((area: DormAreaVO) => {
if (campusMap.has(area.campusId)) {
campusMap.get(area.campusId).children.push({
id: area.id,
areaName: area.areaName
});
}
});
// 转换为树形结构
formAreaTreeData.value = Array.from(campusMap.values());
}
};
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
};
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
dormBuildingFormRef.value?.resetFields();
};
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
@@ -363,7 +235,7 @@ const resetQuery = () => {
/** 多选框选中数据 */
const handleSelectionChange = (selection: DormBuildingVO[]) => {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
@@ -380,10 +252,6 @@ const handleUpdate = async (row?: DormBuildingVO) => {
const _id = row?.id || ids.value[0];
const res = await getDormBuilding(_id);
Object.assign(form.value, res.data);
// 加载表单中的宿舍区数据
if (form.value.campusId) {
loadFormAreaTreeData(form.value.campusId);
}
dialog.visible = true;
dialog.title = '修改宿舍楼信息';
};
@@ -394,13 +262,22 @@ const submitForm = () => {
if (valid) {
buttonLoading.value = true;
if (form.value.id) {
await updateDormBuilding(form.value).finally(() => (buttonLoading.value = false));
await updateDormBuilding(form.value)
.then(() => {
ElMessage.success('修改成功');
dialog.visible = false;
getList();
})
.finally(() => (buttonLoading.value = false));
} else {
await addDormBuilding(form.value).finally(() => (buttonLoading.value = false));
await addDormBuilding(form.value)
.then(() => {
ElMessage.success('新增成功');
dialog.visible = false;
getList();
})
.finally(() => (buttonLoading.value = false));
}
proxy?.$modal.msgSuccess('操作成功');
dialog.visible = false;
await getList();
}
});
};
@@ -408,148 +285,48 @@ const submitForm = () => {
/** 删除按钮操作 */
const handleDelete = async (row?: DormBuildingVO) => {
const _ids = row?.id || ids.value;
let confirmMsg = '';
if (row) {
confirmMsg = `是否确认删除宿舍楼信息编号为"${row?.buildingName}"的数据项?`;
} else if (Array.isArray(ids.value) && ids.value.length > 1) {
confirmMsg = `是否确认删除选中的${ids.value.length}个宿舍楼信息?`;
} else {
confirmMsg = '是否确认删除选中的宿舍楼信息?';
}
await proxy?.$modal.confirm(confirmMsg).finally(() => (loading.value = false));
await delDormBuilding(_ids);
proxy?.$modal.msgSuccess('删除成功');
await getList();
await ElMessageBox.confirm('是否确认删除宿舍楼信息编号为"' + _ids + '"的数据项?')
.then(() => {
loading.value = true;
return delDormBuilding(_ids);
})
.then(() => {
loading.value = false;
getList();
ElMessage.success('删除成功');
})
.catch(() => {
loading.value = false;
});
};
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download(
'sslxx/dormBuilding/export',
{
...queryParams.value
},
`dormBuilding_${new Date().getTime()}.xlsx`
);
/** 重置操作表单 */
const reset = () => {
form.value = { ...initFormData };
dormBuildingFormRef.value?.resetFields();
};
/** 取消按钮 */
const cancel = () => {
dialog.visible = false;
reset();
};
onMounted(() => {
getList();
loadCampusOptions().then(() => {
loadAreaTreeData();
});
loadCampusOptions();
});
</script>
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 8px;
background-color: #f5f5f5;
}
/* 表格卡片布局 */
.table-card {
margin-top: 0;
}
.table-card :deep(.el-card__body) {
padding: 0px 20px 20px 20px !important;
}
/* 移除按钮栏底部边框 */
.button-card :deep(.el-card__header) {
border-bottom: none !important;
}
.image-gallery {
display: flex;
flex-wrap: wrap;
gap: 12px;
padding: 8px;
}
.image-item {
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #dcdfe6;
border-radius: 6px;
background-color: #fff;
padding: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.image-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.no-image {
display: flex;
align-items: center;
justify-content: center;
width: 120px;
height: 80px;
border: 1px dashed #dcdfe6;
border-radius: 4px;
background-color: #fafafa;
}
.no-image-text {
color: #909399;
font-size: 12px;
text-align: center;
}
.table-no-image {
display: flex;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
border: 1px dashed #dcdfe6;
border-radius: 4px;
background-color: #fafafa;
margin: 0 auto;
}
.table-no-image-text {
color: #909399;
font-size: 10px;
text-align: center;
}
.view-text {
font-size: 14px;
color: #303133;
line-height: 1.5;
}
.view-description {
padding: 12px;
border: 1px solid #e4e7ed;
border-radius: 4px;
width: 100%;
height: 200px;
font-size: 14px;
color: #303133;
line-height: 1.6;
white-space: pre-wrap;
word-break: break-word;
overflow-y: auto;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
.el-form-item {
margin-bottom: 0 !important;
}
text-align: left;
}
.fixed-width-input {
width: 100%;
}
+123 -45
View File
@@ -98,52 +98,73 @@
</el-row>
</template>
<!-- 楼层卡片 -->
<div v-for="floor in floorList" :key="floor" class="mb-4">
<el-divider>
<el-tag type="success" effect="dark" class="mr-2">{{ floor }}层</el-tag>
<el-button type="primary" icon="Plus" size="small" @click="handleAddAllRoom(floor)">自动添加房间</el-button>
<el-button type="primary" icon="Plus" size="small" @click="handleAddRoom(floor)">添加房间</el-button>
</el-divider>
<div class="room-container">
<el-empty v-if="getRoomsByFloor(floor).length === 0" description="暂无房间数据" />
<div v-else class="room-grid">
<el-card
v-for="room in getRoomsByFloor(floor)"
:key="room.id"
class="room-card"
:class="{ 'room-disabled': room.roomStatus !== '0' }"
>
<template #header>
<div class="flex items-center justify-between">
<span class="font-bold">{{ room.roomName }}</span>
<div>
<el-tag v-if="room.roomStatus === '0'" type="success" size="small">正常</el-tag>
<el-tag v-else-if="room.roomStatus === '1'" type="warning" size="small">维修中</el-tag>
<el-tag v-else type="danger" size="small">停用</el-tag>
</div>
</div>
</template>
<div class="room-info">
<div class="mb-2">
<span class="text-gray-500">类型:</span>{{ room.roomType === '1' ? '标准间' : room.roomType === '2' ? '单人间' : '套间' }}
</div>
<div class="mb-2"><span class="text-gray-500">床位:</span>{{ room.currentBeds || 0 }}/{{ room.actualBeds }}</div>
<div class="mb-2"><span class="text-gray-500">排序:</span>{{ room.sort || 0 }}</div>
<div class="flex justify-between mt-4">
<div>
<el-button type="primary" link size="small" @click="handleManageBeds(room)">床位管理</el-button>
</div>
<div>
<el-button type="primary" link icon="Edit" @click="handleEditRoom(room)" />
<el-button type="danger" link icon="Delete" @click="handleDeleteRoom(room)" />
</div>
</div>
</div>
</el-card>
<!-- 右侧房间信息区域 -->
<div class="room-info-area">
<!-- 加载状态 -->
<div v-if="roomLoading" class="loading-container">
<div class="loading-content">
<el-icon class="loading-icon" :size="32">
<Loading />
</el-icon>
<p class="loading-text">正在加载房间数据...</p>
</div>
</div>
<!-- 房间数据展示 -->
<div v-else-if="roomList.length > 0" class="room-display">
<div v-for="floor in floorList" :key="floor" class="mb-4">
<el-divider>
<el-tag type="success" effect="dark" class="mr-2">{{ floor }}层</el-tag>
<el-button type="primary" icon="Plus" size="small" @click="handleAddAllRoom(floor)">自动添加房间</el-button>
<el-button type="primary" icon="Plus" size="small" @click="handleAddRoom(floor)">添加房间</el-button>
</el-divider>
<div class="room-container">
<el-empty v-if="getRoomsByFloor(floor).length === 0" description="暂无房间数据" />
<div v-else class="room-grid">
<el-card
v-for="room in getRoomsByFloor(floor)"
:key="room.id"
class="room-card"
:class="{ 'room-disabled': room.roomStatus !== '0' }"
>
<template #header>
<div class="flex items-center justify-between">
<span class="font-bold">{{ room.roomName }}</span>
<div>
<el-tag v-if="room.roomStatus === '0'" type="success" size="small">正常</el-tag>
<el-tag v-else-if="room.roomStatus === '1'" type="warning" size="small">维修中</el-tag>
<el-tag v-else type="danger" size="small">停用</el-tag>
</div>
</div>
</template>
<div class="room-info">
<div class="mb-2">
<span class="text-gray-500">类型:</span
>{{ room.roomType === '1' ? '标准间' : room.roomType === '2' ? '单人间' : '套间' }}
</div>
<div class="mb-2"><span class="text-gray-500">床位:</span>{{ room.currentBeds || 0 }}/{{ room.actualBeds }}</div>
<div class="mb-2"><span class="text-gray-500">排序:</span>{{ room.sort || 0 }}</div>
<div class="flex justify-between mt-4">
<div>
<el-button type="primary" size="small" @click="handleManageBeds(room)">床位管理</el-button>
</div>
<div>
<el-button type="primary" link size="small" icon="Edit" @click="handleEditRoom(room)" />
<el-button type="danger" link size="small" icon="Delete" @click="handleDeleteRoom(room)" />
</div>
</div>
</div>
</el-card>
</div>
</div>
</div>
</div>
<!-- 空状态展示 -->
<div v-else class="empty-state">
<el-empty description="请先选择左侧宿舍楼查看房间信息" />
</div>
</div>
</el-card>
</template>
@@ -213,7 +234,7 @@
<script setup name="DormRoomManagement" lang="ts">
import { ref, computed, reactive, onMounted, nextTick, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { InfoFilled, Search } from '@element-plus/icons-vue';
import { InfoFilled, Search, Loading } from '@element-plus/icons-vue';
import { DormTreeNode, BedInfo, DormQuery, BedForm } from '@/api/xg/xgss/xsssfjgl/types';
import { DormRoomVO, DormRoomQuery } from '@/api/xg/xgss/dormRoom/types';
import { getDormTree } from '@/api/xg/xgss/xsssfjgl';
@@ -230,6 +251,8 @@ const dormTreeRef = ref();
const queryFormRef = ref();
const buildingName = ref('');
const showSearch = ref(true);
// 添加房间数据加载状态
const roomLoading = ref(false);
const animate = reactive({
searchAnimate: {
enter: 'animate__animated animate__fadeInDown',
@@ -356,6 +379,9 @@ const handleNodeClick = (data: DormTreeNode) => {
const loadRoomData = async () => {
if (!queryParams.buildingId) return;
// 开始加载,设置loading状态
roomLoading.value = true;
try {
const res = await listDormRoom(queryParams);
roomList.value = res?.rows;
@@ -375,6 +401,9 @@ const loadRoomData = async () => {
);
} catch (error) {
console.error('获取房间列表失败', error);
} finally {
// 加载完成,取消loading状态
roomLoading.value = false;
}
};
@@ -862,4 +891,53 @@ watch(
min-width: 100%; /* 确保树至少占满容器宽度 */
width: max-content; /* 允许内容扩展 */
}
/* 加载状态样式 */
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 400px;
background: #fafafa;
border-radius: 8px;
border: 1px dashed #d9d9d9;
margin: 20px 0;
}
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.loading-icon {
color: var(--el-color-primary);
animation: rotate 1s linear infinite;
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.loading-text {
font-size: 14px;
color: var(--el-text-color-regular);
margin: 0;
font-weight: 500;
}
/* 空状态样式 */
.empty-state {
min-height: 400px;
display: flex;
align-items: center;
justify-content: center;
}
</style>
+2 -1
View File
@@ -103,8 +103,8 @@
</el-table>
</el-card>
</el-tab-pane>
<!-- 隐藏宿舍区管理员tab页
<el-tab-pane label="宿舍区管理员" name="area">
<!-- 检索栏(宿舍区管理员) -->
<div class="mb-[10px]">
<el-card shadow="hover">
<el-form :inline="true" :model="areaSearchForm" class="search-form">
@@ -155,6 +155,7 @@
</el-table>
</el-card>
</el-tab-pane>
-->
<el-tab-pane label="宿舍楼管理员" name="building">
<!-- 检索栏(宿舍楼管理员) -->
<div class="mb-[10px]">