提交宿舍楼树形结构的代码

This commit is contained in:
zlwu committed 2025-07-22 10:20:15 +08:00
1 parent 12b4b58647
commit 92f2598017
21 files changed
+1156 -515

No files matched your search

@@ -0,0 +1,39 @@
package org.dromara.xg.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
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.bo.DormBuildingTreeBo;
import org.dromara.xg.service.IXgDormBuildingTreeService;
/**
* 宿舍楼树形结构控制器
*
* @author wuzilong
* @date 2025-07-03
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/xgss/dormBuildingTree")
public class XgDormBuildingTreeController extends BaseController {
private final IXgDormBuildingTreeService xgDormBuildingTreeService;
/**
* 获取宿舍楼树形结构数据
* 校区-宿舍区-宿舍楼三级结构,校区和宿舍区不可选,只有宿舍楼可以选择
*/
// @SaCheckPermission("xgss:dormBuildingTree:list")
@GetMapping("/list")
public R<List<DormBuildingTreeVo>> getDormBuildingTree(DormBuildingTreeBo bo) {
List<DormBuildingTreeVo> treeData = xgDormBuildingTreeService.getDormBuildingTree(bo);
return R.ok(treeData);
}
}
@@ -73,4 +73,9 @@ public class XgDormBuilding extends TenantEntity {
*/
private String remark;
/**
* 总楼层数
*/
private Integer floors;
}
@@ -0,0 +1,36 @@
package org.dromara.xg.domain.bo;
import java.io.Serializable;
import lombok.Data;
/**
* 宿舍楼树查询业务对象
*
* @author wuzilong
* @date 2025-07-03
*/
@Data
public class DormBuildingTreeBo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 校区ID
*/
private Long campusId;
/**
* 宿舍区ID
*/
private Long areaId;
/**
* 宿舍楼名称(模糊查询)
*/
private String buildingName;
/**
* 宿舍楼状态
*/
private String status;
}
@@ -65,5 +65,9 @@ public class XgDormBuildingBo extends BaseEntity {
*/
private Long sortOrder;
/**
* 总楼层数
*/
private Integer floors;
}
@@ -0,0 +1,105 @@
package org.dromara.xg.domain.vo;
import java.io.Serializable;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
/**
* 宿舍楼信息VO
*
* @author wuzilong
* @date 2025-07-03
*/
@Data
public class DormBuildingInfoVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 宿舍楼ID
*/
private Long id;
/**
* 宿舍楼名称
*/
private String buildingName;
/**
* 宿舍楼编号
*/
private String buildingCode;
/**
* 所属校区ID
*/
private Long campusId;
/**
* 校区名称
*/
private String campusName;
/**
* 所属宿舍区ID
*/
private Long areaId;
/**
* 宿舍区名称
*/
private String areaName;
/**
* 楼层数
*/
private Integer floors;
/**
* 房间数
*/
private Integer roomCount;
/**
* 床位数
*/
private Integer bedCount;
/**
* 已使用床位数
*/
private Integer usedBedCount;
/**
* 状态(0正常 1维修中 2停用)
*/
private String status;
/**
* 备注
*/
private String remark;
/**
* 创建者
*/
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;
}
@@ -0,0 +1,49 @@
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-07-03
*/
@Data
public class DormBuildingTreeVo implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 节点ID
*/
private Long id;
/**
* 节点名称
*/
private String label;
/**
* 节点类型 (campus:校区, area:宿舍区, building:宿舍楼)
*/
private String type;
/**
* 是否禁用节点(校区和宿舍区节点禁用点击)
*/
private Boolean disabled;
/**
* 宿舍楼层数(仅宿舍楼节点有此字段)
*/
private Integer buildingFloors;
/**
* 子节点
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private List<DormBuildingTreeVo> children;
}
@@ -87,6 +87,12 @@ public class XgDormBuildingVo implements Serializable {
@ExcelProperty(value = "排序号")
private Long sortOrder;
/**
* 总楼层数
*/
@ExcelProperty(value = "总楼层数")
private Integer floors;
// 基础字段
private Long createBy; // 创建者
private Date createTime; // 创建时间
@@ -0,0 +1,84 @@
package org.dromara.xg.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.dromara.xg.domain.vo.DormBuildingInfoVo;
import org.dromara.xg.domain.bo.DormBuildingTreeBo;
/**
* 宿舍楼树形结构Mapper接口
*
* @author wuzilong
* @date 2025-07-03
*/
public interface XgDormBuildingTreeMapper {
/**
* 获取所有校区信息
*
* @return 校区列表
*/
List<DormBuildingInfoVo> selectCampusList();
/**
* 获取所有宿舍区信息
*
* @return 宿舍区列表
*/
List<DormBuildingInfoVo> selectAreaList();
/**
* 获取所有宿舍楼信息
*
* @return 宿舍楼列表
*/
List<DormBuildingInfoVo> selectBuildingList(DormBuildingTreeBo bo);
/**
* 获取全校管理员权限的宿舍楼信息
*
* @param userId 用户ID
* @param bo 查询参数
* @return 宿舍楼列表
*/
List<DormBuildingInfoVo> selectBuildingsBySchoolAdmin(@Param("userId") Long userId,
@Param("bo") DormBuildingTreeBo bo);
/**
* 获取校区管理员权限的宿舍楼信息
*
* @param userId 用户ID
* @param bo 查询参数
* @return 宿舍楼列表
*/
List<DormBuildingInfoVo> selectBuildingsByCampusAdmin(@Param("userId") Long userId,
@Param("bo") DormBuildingTreeBo bo);
/**
* 获取宿舍区管理员权限的宿舍楼信息
*
* @param userId 用户ID
* @param bo 查询参数
* @return 宿舍楼列表
*/
List<DormBuildingInfoVo> selectBuildingsByAreaAdmin(@Param("userId") Long userId,
@Param("bo") DormBuildingTreeBo bo);
/**
* 获取宿舍楼管理员权限的宿舍楼信息
*
* @param userId 用户ID
* @param bo 查询参数
* @return 宿舍楼列表
*/
List<DormBuildingInfoVo> selectBuildingsByBuildingAdmin(@Param("userId") Long userId,
@Param("bo") DormBuildingTreeBo bo);
/**
* 检查用户是否有宿舍管理权限
*
* @param userId 用户ID
* @return 管理员类型列表 (1全校管理员 2校区管理员 3宿舍区管理员 4宿舍楼管理员)
*/
List<String> checkUserDormManagerType(@Param("userId") Long userId);
}
@@ -0,0 +1,23 @@
package org.dromara.xg.service;
import java.util.List;
import org.dromara.xg.domain.vo.DormBuildingTreeVo;
import org.dromara.xg.domain.bo.DormBuildingTreeBo;
/**
* 宿舍楼树形结构服务接口
*
* @author wuzilong
* @date 2025-07-03
*/
public interface IXgDormBuildingTreeService {
/**
* 获取宿舍楼树形结构数据
*
* @param bo 查询参数
* @return 树形结构数据
*/
List<DormBuildingTreeVo> getDormBuildingTree(DormBuildingTreeBo bo);
}
@@ -0,0 +1,155 @@
package org.dromara.xg.service.impl;
import java.util.*;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
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.DormBuildingInfoVo;
import org.dromara.xg.domain.bo.DormBuildingTreeBo;
import org.dromara.common.satoken.utils.LoginHelper;
/**
* 宿舍楼树形结构服务实现
*
* @author wuzilong
* @date 2025-07-03
*/
@RequiredArgsConstructor
@Service
public class XgDormBuildingTreeServiceImpl implements IXgDormBuildingTreeService {
private final XgDormBuildingTreeMapper dormBuildingTreeMapper;
/**
* 获取宿舍楼树形结构数据
*
* @param bo 查询参数
* @return 树形结构数据
*/
@Override
public List<DormBuildingTreeVo> getDormBuildingTree(DormBuildingTreeBo bo) {
// 获取当前登录用户ID
Long userId = LoginHelper.getUserId();
// 查询用户管理权限类型
List<String> managerTypes = dormBuildingTreeMapper.checkUserDormManagerType(userId);
if (managerTypes == null || managerTypes.isEmpty()) {
// 没有宿舍管理权限,返回空列表
return new ArrayList<>();
}
// 获取所有宿舍楼数据(根据权限过滤)
List<DormBuildingInfoVo> buildingList = new ArrayList<>();
// 按照权限分别查询并合并结果(支持用户拥有多种角色的情况)
if (managerTypes.contains("1")) {
// 全校管理员,获取所有宿舍楼
buildingList = dormBuildingTreeMapper.selectBuildingsBySchoolAdmin(userId, bo);
// 全校管理员可以查看所有宿舍楼,无需继续查询其他权限
} else {
// 对于非全校管理员,合并多种权限的结果
Set<Long> addedBuildingIds = new HashSet<>(); // 用于去重
if (managerTypes.contains("2")) {
// 校区管理员,获取管理的校区下的宿舍楼
List<DormBuildingInfoVo> campusBuildings = dormBuildingTreeMapper.selectBuildingsByCampusAdmin(userId,
bo);
for (DormBuildingInfoVo building : campusBuildings) {
if (addedBuildingIds.add(building.getId())) {
buildingList.add(building);
}
}
}
if (managerTypes.contains("3")) {
// 宿舍区管理员,获取管理的宿舍区下的宿舍楼
List<DormBuildingInfoVo> areaBuildings = dormBuildingTreeMapper.selectBuildingsByAreaAdmin(userId, bo);
for (DormBuildingInfoVo building : areaBuildings) {
if (addedBuildingIds.add(building.getId())) {
buildingList.add(building);
}
}
}
if (managerTypes.contains("4")) {
// 宿舍楼管理员,获取管理的宿舍楼
List<DormBuildingInfoVo> directBuildings = dormBuildingTreeMapper.selectBuildingsByBuildingAdmin(userId,
bo);
for (DormBuildingInfoVo building : directBuildings) {
if (addedBuildingIds.add(building.getId())) {
buildingList.add(building);
}
}
}
}
// 如果没有获取到宿舍楼数据,返回空列表
if (buildingList.isEmpty()) {
return new ArrayList<>();
}
// 获取相关校区和宿舍区数据
List<DormBuildingInfoVo> campusList = dormBuildingTreeMapper.selectCampusList();
List<DormBuildingInfoVo> areaList = dormBuildingTreeMapper.selectAreaList();
// 构建校区节点集合(第一层)
Map<Long, DormBuildingTreeVo> campusMap = new LinkedHashMap<>();
for (DormBuildingInfoVo campus : campusList) {
// 校区只保留有权限的宿舍楼所在的校区
if (buildingList.stream().anyMatch(b -> Objects.equals(b.getCampusId(), campus.getId()))) {
DormBuildingTreeVo campusNode = new DormBuildingTreeVo();
campusNode.setId(campus.getId());
campusNode.setLabel(campus.getBuildingName());
campusNode.setType("campus");
campusNode.setDisabled(true); // 校区节点禁止点击
campusNode.setChildren(new ArrayList<>());
campusMap.put(campus.getId(), campusNode);
}
}
// 构建宿舍区节点(第二层)
Map<Long, DormBuildingTreeVo> areaMap = new HashMap<>();
for (DormBuildingInfoVo area : areaList) {
// 宿舍区只保留有权限的宿舍楼所在的宿舍区
if (buildingList.stream().anyMatch(b -> Objects.equals(b.getAreaId(), area.getId()))) {
DormBuildingTreeVo areaNode = new DormBuildingTreeVo();
areaNode.setId(area.getId());
areaNode.setLabel(area.getBuildingName());
areaNode.setType("area");
areaNode.setDisabled(true); // 宿舍区节点禁止点击
areaNode.setChildren(new ArrayList<>());
areaMap.put(area.getId(), areaNode);
// 将宿舍区节点添加到对应的校区节点下
DormBuildingTreeVo campusNode = campusMap.get(area.getCampusId());
if (campusNode != null) {
campusNode.getChildren().add(areaNode);
}
}
}
// 构建宿舍楼节点(第三层)
for (DormBuildingInfoVo building : buildingList) {
DormBuildingTreeVo buildingNode = new DormBuildingTreeVo();
buildingNode.setId(building.getId());
buildingNode.setLabel(building.getBuildingName());
buildingNode.setType("building");
buildingNode.setDisabled(false); // 宿舍楼节点可点击
buildingNode.setBuildingFloors(building.getFloors());
// 将宿舍楼节点添加到对应的宿舍区节点下
DormBuildingTreeVo areaNode = areaMap.get(building.getAreaId());
if (areaNode != null) {
areaNode.getChildren().add(buildingNode);
}
}
// 返回树形结构的根节点列表(校区节点列表)
return new ArrayList<>(campusMap.values());
}
}
@@ -15,6 +15,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
c.campus_name,
b.building_type,
b.building_description,
b.floors,
b.sort_order,
b.deleted,
b.remark,
@@ -0,0 +1,297 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.xg.mapper.XgDormBuildingTreeMapper">
<resultMap id="BuildingInfoMap" type="org.dromara.xg.domain.vo.DormBuildingInfoVo">
<id property="id" column="id"/>
<result property="buildingName" column="building_name"/>
<result property="buildingCode" column="building_code"/>
<result property="campusId" column="campus_id"/>
<result property="campusName" column="campus_name"/>
<result property="areaId" column="area_id"/>
<result property="areaName" column="area_name"/>
<result property="floors" column="floors"/>
<result property="remark" column="remark"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<!-- 获取所有校区信息 -->
<select id="selectCampusList" resultMap="BuildingInfoMap">
SELECT
id,
campus_name as building_name,
'0' as status
FROM
xg_dorm_campus
WHERE
deleted = '0'
</select>
<!-- 获取所有宿舍区信息 -->
<select id="selectAreaList" resultMap="BuildingInfoMap">
SELECT
a.id,
a.area_name as building_name,
a.campus_id,
c.campus_name,
'0' as status
FROM
xg_dorm_area a
LEFT JOIN
xg_dorm_campus c ON a.campus_id = c.id
WHERE
a.deleted = '0'
</select>
<!-- 获取所有宿舍楼信息 -->
<select id="selectBuildingList" parameterType="org.dromara.xg.domain.bo.DormBuildingTreeBo" resultMap="BuildingInfoMap">
SELECT
b.id,
b.building_name,
b.building_code,
b.campus_id,
c.campus_name,
b.area_id,
a.area_name,
b.floors,
b.remark,
b.create_by,
b.create_time,
b.update_by,
b.update_time
FROM
xg_dorm_building b
LEFT JOIN
xg_dorm_campus c ON b.campus_id = c.id
LEFT JOIN
xg_dorm_area a ON b.area_id = a.id
WHERE
b.deleted = '0'
<if test="campusId != null">
AND b.campus_id = #{campusId}
</if>
<if test="areaId != null">
AND b.area_id = #{areaId}
</if>
<if test="buildingName != null and buildingName != ''">
AND b.building_name like concat('%', #{buildingName}, '%')
</if>
<if test="status != null and status != ''">
AND b.status = #{status}
</if>
</select>
<!-- 检查用户是否有宿舍管理权限 -->
<select id="checkUserDormManagerType" resultType="String">
SELECT
DISTINCT manager_type
FROM
xg_dorm_manager
WHERE
user_id = #{userId}
AND deleted = '0'
</select>
<!-- 获取全校管理员权限的宿舍楼信息 -->
<select id="selectBuildingsBySchoolAdmin" resultMap="BuildingInfoMap">
SELECT
b.id,
b.building_name,
b.building_code,
b.campus_id,
c.campus_name,
b.area_id,
a.area_name,
b.floors,
b.remark,
b.create_by,
b.create_time,
b.update_by,
b.update_time
FROM
xg_dorm_building b
LEFT JOIN
xg_dorm_campus c ON b.campus_id = c.id
LEFT JOIN
xg_dorm_area a ON b.area_id = a.id
WHERE
b.deleted = '0'
AND EXISTS (
SELECT 1 FROM xg_dorm_manager m
WHERE m.user_id = #{userId}
AND m.manager_type = '1'
AND m.deleted = '0'
)
<if test="bo.campusId != null">
AND b.campus_id = #{bo.campusId}
</if>
<if test="bo.areaId != null">
AND b.area_id = #{bo.areaId}
</if>
<if test="bo.buildingName != null and bo.buildingName != ''">
AND b.building_name like concat('%', #{bo.buildingName}, '%')
</if>
<if test="bo.status != null and bo.status != ''">
AND b.status = #{bo.status}
</if>
</select>
<!-- 获取校区管理员权限的宿舍楼信息 -->
<select id="selectBuildingsByCampusAdmin" resultMap="BuildingInfoMap">
SELECT
b.id,
b.building_name,
b.building_code,
b.campus_id,
c.campus_name,
b.area_id,
a.area_name,
b.floors,
b.remark,
b.create_by,
b.create_time,
b.update_by,
b.update_time
FROM
xg_dorm_building b
LEFT JOIN
xg_dorm_campus c ON b.campus_id = c.id
LEFT JOIN
xg_dorm_area a ON b.area_id = a.id
WHERE
b.deleted = '0'
AND b.campus_id IN (
SELECT
auth_source
FROM
xg_dorm_manager m
JOIN
xg_dorm_manager_source s ON m.id = s.manager_id
WHERE
m.user_id = #{userId}
AND m.manager_type = '2'
AND m.deleted = '0'
AND s.deleted = '0'
)
<if test="bo.campusId != null">
AND b.campus_id = #{bo.campusId}
</if>
<if test="bo.areaId != null">
AND b.area_id = #{bo.areaId}
</if>
<if test="bo.buildingName != null and bo.buildingName != ''">
AND b.building_name like concat('%', #{bo.buildingName}, '%')
</if>
<if test="bo.status != null and bo.status != ''">
AND b.status = #{bo.status}
</if>
</select>
<!-- 获取宿舍区管理员权限的宿舍楼信息 -->
<select id="selectBuildingsByAreaAdmin" resultMap="BuildingInfoMap">
SELECT
b.id,
b.building_name,
b.building_code,
b.campus_id,
c.campus_name,
b.area_id,
a.area_name,
b.floors,
b.remark,
b.create_by,
b.create_time,
b.update_by,
b.update_time
FROM
xg_dorm_building b
LEFT JOIN
xg_dorm_campus c ON b.campus_id = c.id
LEFT JOIN
xg_dorm_area a ON b.area_id = a.id
WHERE
b.deleted = '0'
AND b.area_id IN (
SELECT
auth_source
FROM
xg_dorm_manager m
JOIN
xg_dorm_manager_source s ON m.id = s.manager_id
WHERE
m.user_id = #{userId}
AND m.manager_type = '3'
AND m.deleted = '0'
AND s.deleted = '0'
)
<if test="bo.campusId != null">
AND b.campus_id = #{bo.campusId}
</if>
<if test="bo.areaId != null">
AND b.area_id = #{bo.areaId}
</if>
<if test="bo.buildingName != null and bo.buildingName != ''">
AND b.building_name like concat('%', #{bo.buildingName}, '%')
</if>
<if test="bo.status != null and bo.status != ''">
AND b.status = #{bo.status}
</if>
</select>
<!-- 获取宿舍楼管理员权限的宿舍楼信息 -->
<select id="selectBuildingsByBuildingAdmin" resultMap="BuildingInfoMap">
SELECT
b.id,
b.building_name,
b.building_code,
b.campus_id,
c.campus_name,
b.area_id,
a.area_name,
b.floors,
b.remark,
b.create_by,
b.create_time,
b.update_by,
b.update_time
FROM
xg_dorm_building b
LEFT JOIN
xg_dorm_campus c ON b.campus_id = c.id
LEFT JOIN
xg_dorm_area a ON b.area_id = a.id
WHERE
b.deleted = '0'
AND b.id IN (
SELECT
auth_source
FROM
xg_dorm_manager m
JOIN
xg_dorm_manager_source s ON m.id = s.manager_id
WHERE
m.user_id = #{userId}
AND m.manager_type = '4'
AND m.deleted = '0'
AND s.deleted = '0'
)
<if test="bo.campusId != null">
AND b.campus_id = #{bo.campusId}
</if>
<if test="bo.areaId != null">
AND b.area_id = #{bo.areaId}
</if>
<if test="bo.buildingName != null and bo.buildingName != ''">
AND b.building_name like concat('%', #{bo.buildingName}, '%')
</if>
<if test="bo.status != null and bo.status != ''">
AND b.status = #{bo.status}
</if>
</select>
</mapper>
@@ -1,62 +1,83 @@
# 宿舍楼房间管理功能开发记录
# 宿舍楼树形结构API开发变更记录
**日期:** 2025-07-03 14:30:25
**开发人员:** Cursor
**功能模块:** 学生宿舍管理系统 - 宿舍楼房间管理
**日期**: 2025-07-03
**作者**: cursor
**模块**: 学生宿舍管理
## 变更内容
### 1. 功能概述
### 1. 新增实体类
开发了宿舍楼房间管理界面,实现了以下功能:
- 左侧树形结构展示校区-宿舍区-宿舍楼的层级关系
- 右侧按楼层展示房间信息,采用卡片式布局
- 支持房间的添加、编辑、删除操作
- 支持床位的添加、编辑、删除操作
- 支持按房间号和状态进行筛选
- 创建 `DormBuildingTreeVo.java`: 实现宿舍楼树形结构VO类,包含id、label、type、disabled、buildingFloors、children等字段
- 创建 `DormBuildingInfoVo.java`: 实现宿舍楼信息VO类,用于数据库查询结果映射
- 创建 `DormBuildingTreeBo.java`: 实现查询参数业务对象类,包含campusId、areaId、buildingName、status等字段
### 2. 文件变更
### 2. 新增Mapper接口和XML
#### 新增文件
- `web/src/views/xsssfjgl/index.vue` - 宿舍楼房间管理主界面
- `web/src/views/xsssfjgl/components/RoomDialog.vue` - 房间编辑对话框组件
- `web/src/views/xsssfjgl/components/BedDialog.vue` - 床位编辑对话框组件
- `web/src/api/xsssfjgl/index.ts` - API接口实现
- `web/src/api/xsssfjgl/types.ts` - API类型定义
- 创建 `XgDormBuildingTreeMapper.java`: 包含获取校区、宿舍区、宿舍楼和检查用户权限等方法
- 创建 `XgDormBuildingTreeMapper.xml`: 实现各种SQL查询,包括权限过滤逻辑
### 3. 技术实现
### 3. 新增服务接口和实现
#### 3.1 树形结构设计
- 采用Element Plus的Tree组件实现
- 配置了disabled属性,使校区和宿舍区节点不可选
- 只有宿舍楼节点可以被选中,并触发右侧数据加载
- 创建 `IXgDormBuildingTreeService.java`: 定义获取宿舍楼树形结构的服务接口
- 创建 `XgDormBuildingTreeServiceImpl.java`: 实现服务接口,包含按权限过滤和构建三级树形结构的逻辑
#### 3.2 房间展示
- 按楼层分组展示房间信息
- 使用Grid布局实现自适应卡片排列
- 每张卡片包含房间号、类型、床位使用情况和操作按钮
### 4. 新增控制器
#### 3.3 床位管理
- 点击"床位管理"按钮弹出对话框
- 表格展示该房间的所有床位信息
- 支持床位的增删改操作
- 创建 `XgDormBuildingTreeController.java`: 提供获取宿舍楼树形结构的REST接口
#### 3.4 数据处理
- 前期使用模拟数据进行界面开发
- 预留了真实API接口,便于后期接入后端服务
- 完整实现了增删改查的前端逻辑
### 5. 新增API文档
## 后续工作
- 创建 `宿舍房间管理API.md`: 记录API接口的使用方法和参数说明
1. 对接后端API,替换模拟数据
2. 根据实际业务需求调整界面细节
3. 增加权限控制逻辑
4. 完善数据校验和错误处理
5. 添加单元测试
### 6. 代码优化与修复 (2025-07-03 16:25:30)
## 注意事项
- 修改 `XgDormBuildingTreeServiceImpl.java`: 优化宿舍楼查询逻辑,支持用户拥有多种角色权限的情况
- 解决问题: 之前代码只会按优先级执行一种角色的查询,现在改为合并多种权限查询的结果
- 实现思路:
- 保留全校管理员最高权限判断(可查看所有宿舍楼)
- 对于其他权限类型,分别查询并合并结果
- 使用HashSet确保宿舍楼不重复
- 修正了字段访问方法,使用getId()而不是getBuildingId()
- 目前使用模拟数据,未与后端对接
- 表单校验规则已基本实现,但可能需要根据业务规则调整
- v-model:show-search 等指令存在linter错误,后续需要修复
- 组件导入和类型引用存在linter错误,需要调整项目配置
### 7. API返回字段修复 (2025-07-23 10:30:15)
- 修改 `XgDormBuildingMapper.xml`: 修复宿舍楼信息列表接口floors字段返回为null的问题
- 解决问题: 宿舍楼信息查询接口未返回floors字段,导致前端显示为null
- 修复详情:
- 在selectPageWithAreaAndCampusName方法的SELECT查询语句中添加了b.floors字段
- 确保与数据库表设计和实体类定义保持一致
- 完整路径: `/backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/sslxx/XgDormBuildingMapper.xml`
## 实现功能
1. **三级树形结构**:
- 第一级:校区(不可点击)
- 第二级:宿舍区(不可点击)
- 第三级:宿舍楼(可点击)
2. **多级数据权限**:
- 全校管理员(manager_type=1):可查看所有宿舍楼
- 校区管理员(manager_type=2):只能查看管理校区的宿舍楼
- 宿舍区管理员(manager_type=3):只能查看管理宿舍区的宿舍楼
- 宿舍楼管理员(manager_type=4):只能查看管理宿舍楼
- **新增:支持一个用户同时拥有多种管理员角色,数据权限取并集**
3. **权限数据关联**:
- 通过 xg_dorm_manager 表获取用户权限类型
- 通过 xg_dorm_manager_source 表获取具体权限范围
## API接口说明
- **接口URL**: `/xgss/dormBuildingTree/list`
- **请求方式**: GET
- **权限要求**: `xgss:dormBuildingTree:list`
- **可选参数**: campusId, areaId, buildingName, status
- **返回格式**: 嵌套的树形结构JSON
## 下一步计划
1. 前端页面改造:将模拟数据替换为真实API调用
2. 单元测试:编写测试用例验证API的功能和权限控制
3. 集成测试:验证API在真实环境中的表现
4. 完善文档:补充其他宿舍管理相关API的文档
+10 -1
View File
@@ -9,7 +9,14 @@
## 学工管理模块
- [班干部管理API](班干部管理API.md)
- [宿舍管理员授权API](宿舍管理员授权API.md)
- [宿舍楼树形结构API](宿舍楼树形结构API.md) - 校区-宿舍区-宿舍楼三级树形结构数据
- [宿舍楼树形结构API](宿舍楼树形结构API.md) - 校区-宿舍区-宿舍楼三级树形结构数据 (旧版API,路径: `/sslxx/dormBuilding/buildingTree`)
## 宿舍管理相关API
| API文档名称 | 说明 | 路径 |
| ---------- | ---- | ---- |
| [宿舍房间管理API](./宿舍房间管理API.md) | 提供宿舍楼树形结构API | `/xgss/dormBuildingTree/list` |
| [宿舍房间管理API](./宿舍房间管理API.md) | 提供宿舍楼列表查询API | `/sslxx/dormBuilding/list` |
## 后勤管理
@@ -41,6 +48,8 @@
## 更新记录
- 2025-07-23:修复宿舍楼列表接口floors字段为null的问题,更新宿舍房间管理API文档
- 2025-07-03:更新宿舍房间管理API,增强多角色权限支持
- 2025-07-20:新增宿舍楼树形结构API文档
- 2025-07-20:新增宿舍管理员授权API文档
- 2025-07-15:新增场地预约管理API文档
+193 -419
View File
@@ -1,79 +1,42 @@
# 宿舍房间管理API文档
**版本:** 1.0.0
**作者:** Cursor
**更新日期:** 2025-07-03
## 目录
- [1. 概述](#1-概述)
- [2. 接口基础信息](#2-接口基础信息)
- [3. 宿舍楼树形结构接口](#3-宿舍楼树形结构接口)
- [4. 房间管理接口](#4-房间管理接口)
- [5. 床位管理接口](#5-床位管理接口)
- [6. 数据字典](#6-数据字典)
- [7. 错误码说明](#7-错误码说明)
- [8. 移动端实现](#8-移动端实现)
- [1. 宿舍楼树形结构](#1-宿舍楼树形结构)
- [1.1 获取宿舍楼树形结构](#11-获取宿舍楼树形结构)
- [2. 宿舍楼信息管理](#2-宿舍楼信息管理)
- [2.1 获取宿舍楼列表](#21-获取宿舍楼列表)
## 1. 概述
## 1. 宿舍楼树形结构
本文档描述了宿舍楼房间管理模块的接口规范,包括宿舍楼树形结构查询、房间增删改查和床位管理等功能。
### 1.1 获取宿舍楼树形结构
### 1.1 应用场景
#### 接口说明
- 学工处管理宿舍楼房间信息
- 学生宿舍分配
- 宿舍房间和床位状态维护
该接口用于获取宿舍楼的树形结构数据,按照校区-宿舍区-宿舍楼三级结构组织。校区和宿舍区节点不可点击,只有宿舍楼节点可以点击。
### 1.2 功能列表
#### 请求方式
- 宿舍楼树形结构查询
- 房间列表查询(支持分页和条件筛选)
- 房间新增、修改、删除
- 床位列表查询
- 床位新增、修改、删除
## 2. 接口基础信息
### 2.1 接口规范
- 接口采用RESTful风格
- 请求和响应数据格式均为JSON
- 所有接口均需要携带Token进行认证
- 接口基础路径: `/xsssfjgl`
### 2.2 统一响应格式
```json
{
"code": 200, // 响应状态码,200表示成功
"msg": "操作成功", // 响应消息
"data": {} // 响应数据,根据接口定义不同而不同
}
```
GET /xgss/dormBuildingTree/list
```
### 2.3 分页查询响应格式
#### 权限要求
```json
{
"code": 200,
"msg": "查询成功",
"rows": [], // 数据列表
"total": 0 // 总记录数
}
```
xgss:dormBuildingTree:list
```
## 3. 宿舍楼树形结构接口
#### 请求参数
### 3.1 获取宿舍树形结构
| 参数名 | 类型 | 必须 | 描述 |
| ------------- | ------- | ---- | -------------------- |
| campusId | Long | 否 | 校区ID,用于筛选 |
| areaId | Long | 否 | 宿舍区ID,用于筛选 |
| buildingName | String | 否 | 宿舍楼名称,模糊查询 |
| status | String | 否 | 宿舍楼状态 |
**接口说明:** 获取校区-宿舍区-宿舍楼的树形结构数据
- **请求方式:** GET
- **请求路径:** `/xsssfjgl/tree`
- **请求参数:** 无
**响应示例:**
#### 返回结果
```json
{
@@ -82,21 +45,44 @@
"data": [
{
"id": 1,
"label": "东校区",
"label": "主校区",
"type": "campus",
"disabled": true,
"children": [
{
"id": 11,
"id": 101,
"label": "东区宿舍",
"type": "area",
"disabled": true,
"children": [
{
"id": 111,
"label": "东区1号楼",
"id": 1001,
"label": "1号楼",
"type": "building",
"disabled": false,
"buildingFloors": 6
},
{
"id": 1002,
"label": "2号楼",
"type": "building",
"disabled": false,
"buildingFloors": 5
}
]
},
{
"id": 102,
"label": "西区宿舍",
"type": "area",
"disabled": true,
"children": [
{
"id": 1003,
"label": "3号楼",
"type": "building",
"disabled": false,
"buildingFloors": 7
}
]
}
@@ -106,384 +92,172 @@
}
```
**字段说明:**
#### 返回参数说明
| 字段名 | 类型 | 说明 |
| ---- | ---- | ---- |
| id | string/number | 节点ID |
| label | string | 节点名称 |
| type | string | 节点类型:campus(校区)、area(宿舍区)、building(宿舍楼) |
| disabled | boolean | 是否禁用选择 |
| children | array | 子节点 |
| buildingFloors | number | 宿舍楼层数,仅type为building时有效 |
| 参数名 | 类型 | 描述 |
| -------------- | ------- | ------------------------------------------------ |
| id | Long | 节点ID |
| label | String | 节点名称 |
| type | String | 节点类型(campus:校区,area:宿舍区,building:宿舍楼) |
| disabled | Boolean | 是否禁用点击(校区和宿舍区为true,宿舍楼为false) |
| buildingFloors | Integer | 宿舍楼层数(只有宿舍楼节点有此字段) |
| children | Array | 子节点列表 |
## 4. 房间管理接口
#### 数据权限说明
### 4.1 获取房间列表
接口会根据当前登录用户的权限进行数据过滤:
**接口说明:** 分页查询房间信息
1. 全校管理员(manager_type=1):可以查看所有校区、宿舍区和宿舍楼数据
2. 校区管理员(manager_type=2):只能查看其管理的校区下的宿舍区和宿舍楼数据
3. 宿舍区管理员(manager_type=3):只能查看其管理的宿舍区下的宿舍楼数据
4. 宿舍楼管理员(manager_type=4):只能查看其管理的宿舍楼数据
5. **多角色支持**:如果用户同时拥有多种管理员角色(如既是校区管理员又是宿舍楼管理员),将会合并所有权限范围内的数据。例如,一个用户同时是A校区管理员和B宿舍楼管理员,则可以看到A校区下的所有宿舍楼以及B宿舍楼。
- **请求方式:** GET
- **请求路径:** `/xsssfjgl/room/list`
- **请求参数:**
权限判断逻辑:
- 通过 xg_dorm_manager 表的 user_id 与当前登录用户匹配,获取用户管理员类型
- 根据 manager_type 确定权限级别
- 通过 xg_dorm_manager_source 表的 auth_source 确定具体权限范围
- **多角色权限合并**:合并不同角色对应的宿舍楼数据,确保不重复
| 参数名 | 必填 | 类型 | 说明 |
| ---- | ---- | ---- | ---- |
| pageNum | 是 | number | 页码,从1开始 |
| pageSize | 是 | number | 每页记录数 |
| buildingId | 是 | string/number | 宿舍楼ID |
| roomName | 否 | string | 房间名称,支持模糊查询 |
| floorNumber | 否 | number | 楼层号 |
| status | 否 | string | 房间状态:0-正常,1-维修中,2-停用 |
#### 错误码
**响应示例:**
| 错误码 | 描述 |
| ------ | -------------------- |
| 401 | 未授权,需要登录 |
| 403 | 无权限访问该接口 |
| 500 | 服务器内部错误 |
#### 使用示例
```javascript
// 前端代码示例(使用axios)
import axios from 'axios';
// 获取宿舍楼树形结构
async function getDormBuildingTree() {
try {
const response = await axios.get('/xgss/dormBuildingTree/list');
if (response.data.code === 200) {
return response.data.data;
}
} catch (error) {
console.error('获取宿舍楼树失败:', error);
}
return [];
}
```
## 2. 宿舍楼信息管理
### 2.1 获取宿舍楼列表
#### 接口说明
该接口用于获取宿舍楼信息的分页列表,包含宿舍楼基本信息以及关联的校区和宿舍区信息。
#### 请求方式
```
GET /sslxx/dormBuilding/list
```
#### 权限要求
```
sslxx:dormBuilding:list
```
#### 请求参数
| 参数名 | 类型 | 必须 | 描述 |
| --------------- | ------- | ---- | ---------------------- |
| pageNum | Integer | 是 | 当前页码,从1开始 |
| pageSize | Integer | 是 | 每页记录数 |
| buildingCode | String | 否 | 宿舍楼编码,模糊查询 |
| buildingName | String | 否 | 宿舍楼名称,模糊查询 |
| areaId | Long | 否 | 宿舍区ID,精确查询 |
| campusId | Long | 否 | 校区ID,精确查询 |
| buildingType | String | 否 | 宿舍楼类型(1男生楼 2女生楼 3混合楼) |
#### 返回结果
```json
{
"code": 200,
"msg": "查询成功",
"total": 1,
"rows": [
{
"roomId": "111101",
"roomName": "101",
"floorNumber": 1,
"buildingId": 111,
"capacity": 4,
"used": 2,
"roomType": "标准间",
"status": "0",
"remark": "",
"createBy": "admin",
"createTime": "2023-01-01 00:00:00",
"updateBy": "admin",
"updateTime": "2023-01-01 00:00:00"
"id": "1947459199879495681",
"buildingCode": "xixi-ssq01-ssl01",
"buildingName": "西溪校区-宿舍区01-宿舍楼01",
"areaId": "1946829650430935041",
"areaName": "西溪校区-宿舍区01",
"campusId": "1946815553291304962",
"campusName": "西溪校区",
"buildingType": "1",
"buildingDescription": "12312",
"sortOrder": 1,
"floors": 6,
"createBy": 1,
"createTime": "2025-07-22 08:50:30",
"updateBy": 1,
"updateTime": "2025-07-22 08:51:06"
}
],
"total": 60
"code": 200,
"msg": "查询成功"
}
```
### 4.2 获取房间详情
#### 返回参数说明
**接口说明:** 根据房间ID获取详细信息
| 参数名 | 类型 | 描述 |
| ------------------ | ------- | ---------------------------------------- |
| id | Long | 宿舍楼主键ID |
| buildingCode | String | 宿舍楼编码 |
| buildingName | String | 宿舍楼名称 |
| areaId | Long | 所属宿舍区ID |
| areaName | String | 所属宿舍区名称 |
| campusId | Long | 所属校区ID |
| campusName | String | 所属校区名称 |
| buildingType | String | 宿舍楼类型(1男生楼 2女生楼 3混合楼) |
| buildingDescription | String | 宿舍楼描述 |
| sortOrder | Integer | 排序号 |
| floors | Integer | 总楼层数 |
| createBy | Long | 创建者ID |
| createTime | Date | 创建时间 |
| updateBy | Long | 更新者ID |
| updateTime | Date | 更新时间 |
- **请求方式:** GET
- **请求路径:** `/xsssfjgl/room/{roomId}`
- **请求参数:**
#### 错误码
| 参数名 | 必填 | 类型 | 说明 |
| ---- | ---- | ---- | ---- |
| roomId | 是 | string/number | 房间ID |
| 错误码 | 描述 |
| ------ | -------------------- |
| 401 | 未授权,需要登录 |
| 403 | 无权限访问该接口 |
| 500 | 服务器内部错误 |
**响应示例:**
#### 使用示例
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"roomId": "111101",
"roomName": "101",
"floorNumber": 1,
"buildingId": 111,
"capacity": 4,
"used": 2,
"roomType": "标准间",
"status": "0",
"remark": "",
"createBy": "admin",
"createTime": "2023-01-01 00:00:00",
"updateBy": "admin",
"updateTime": "2023-01-01 00:00:00"
```javascript
// 前端代码示例(使用axios)
import axios from 'axios';
// 获取宿舍楼列表
async function getDormBuildingList(params) {
try {
const response = await axios.get('/sslxx/dormBuilding/list', {
params: {
pageNum: 1,
pageSize: 10,
...params
}
});
if (response.data.code === 200) {
return response.data;
}
} catch (error) {
console.error('获取宿舍楼列表失败:', error);
}
}
```
### 4.3 新增房间
**接口说明:** 新增宿舍房间
- **请求方式:** POST
- **请求路径:** `/xsssfjgl/room`
- **请求参数:**
```json
{
"roomName": "101",
"floorNumber": 1,
"buildingId": 111,
"capacity": 4,
"roomType": "标准间",
"status": "0",
"remark": "新增房间"
}
```
**响应示例:**
```json
{
"code": 200,
"msg": "操作成功"
}
```
### 4.4 修改房间
**接口说明:** 修改宿舍房间信息
- **请求方式:** PUT
- **请求路径:** `/xsssfjgl/room`
- **请求参数:**
```json
{
"roomId": "111101",
"roomName": "101",
"floorNumber": 1,
"buildingId": 111,
"capacity": 4,
"roomType": "标准间",
"status": "0",
"remark": "修改房间信息"
}
```
**响应示例:**
```json
{
"code": 200,
"msg": "操作成功"
}
```
### 4.5 删除房间
**接口说明:** 删除宿舍房间
- **请求方式:** DELETE
- **请求路径:** `/xsssfjgl/room/{roomIds}`
- **请求参数:**
| 参数名 | 必填 | 类型 | 说明 |
| ---- | ---- | ---- | ---- |
| roomIds | 是 | string | 房间ID,多个ID用逗号分隔 |
**响应示例:**
```json
{
"code": 200,
"msg": "操作成功"
}
```
## 5. 床位管理接口
### 5.1 获取床位列表
**接口说明:** 查询指定房间的床位列表
- **请求方式:** GET
- **请求路径:** `/xsssfjgl/bed/list`
- **请求参数:**
| 参数名 | 必填 | 类型 | 说明 |
| ---- | ---- | ---- | ---- |
| roomId | 是 | string/number | 房间ID |
**响应示例:**
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{
"bedId": "111101-1",
"bedName": "1号床",
"roomId": "111101",
"status": "0",
"studentId": null,
"studentName": null,
"remark": "",
"createBy": "admin",
"createTime": "2023-01-01 00:00:00",
"updateBy": "admin",
"updateTime": "2023-01-01 00:00:00"
},
{
"bedId": "111101-2",
"bedName": "2号床",
"roomId": "111101",
"status": "1",
"studentId": 10001,
"studentName": "张三",
"remark": "",
"createBy": "admin",
"createTime": "2023-01-01 00:00:00",
"updateBy": "admin",
"updateTime": "2023-01-01 00:00:00"
}
]
}
```
### 5.2 新增床位
**接口说明:** 新增床位
- **请求方式:** POST
- **请求路径:** `/xsssfjgl/bed`
- **请求参数:**
```json
{
"bedName": "3号床",
"roomId": "111101",
"status": "0",
"remark": "新增床位"
}
```
**响应示例:**
```json
{
"code": 200,
"msg": "操作成功"
}
```
### 5.3 修改床位
**接口说明:** 修改床位信息
- **请求方式:** PUT
- **请求路径:** `/xsssfjgl/bed`
- **请求参数:**
```json
{
"bedId": "111101-3",
"bedName": "3号床",
"roomId": "111101",
"status": "1",
"remark": "修改床位信息"
}
```
**响应示例:**
```json
{
"code": 200,
"msg": "操作成功"
}
```
### 5.4 删除床位
**接口说明:** 删除床位
- **请求方式:** DELETE
- **请求路径:** `/xsssfjgl/bed/{bedIds}`
- **请求参数:**
| 参数名 | 必填 | 类型 | 说明 |
| ---- | ---- | ---- | ---- |
| bedIds | 是 | string | 床位ID,多个ID用逗号分隔 |
**响应示例:**
```json
{
"code": 200,
"msg": "操作成功"
}
```
## 6. 数据字典
### 6.1 房间状态
| 状态码 | 说明 |
| ---- | ---- |
| 0 | 正常 |
| 1 | 维修中 |
| 2 | 停用 |
### 6.2 床位状态
| 状态码 | 说明 |
| ---- | ---- |
| 0 | 空闲 |
| 1 | 已分配 |
| 2 | 维修中 |
### 6.3 房间类型
| 类型 | 说明 |
| ---- | ---- |
| 标准间 | 标准宿舍,通常4-6人 |
| 单人间 | 单人宿舍 |
| 套间 | 带独立卫浴的宿舍 |
## 7. 错误码说明
| 错误码 | 说明 |
| ---- | ---- |
| 200 | 操作成功 |
| 400 | 请求参数错误 |
| 401 | 未授权 |
| 403 | 禁止访问 |
| 404 | 资源不存在 |
| 500 | 系统内部错误 |
| 1001 | 房间名称已存在 |
| 1002 | 房间包含已分配的床位,无法删除 |
| 1003 | 床位已分配给学生,无法删除 |
## 8. 移动端实现
### 8.1 移动端适配说明
移动端将实现以下功能:
- 宿舍楼列表查看(无树形结构,改用列表+筛选)
- 房间信息查看
- 床位信息查看
暂不支持在移动端进行房间和床位的增删改操作,仅支持查询功能。
### 8.2 特殊API
移动端将使用与PC端相同的API,但增加以下特殊接口:
**获取宿舍楼列表:**
- **请求方式:** GET
- **请求路径:** `/xsssfjgl/mobile/buildings`
- **请求参数:**
| 参数名 | 必填 | 类型 | 说明 |
| ---- | ---- | ---- | ---- |
| campusId | 否 | string/number | 校区ID |
| areaId | 否 | string/number | 宿舍区ID |
**响应示例:**
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{
"buildingId": 111,
"buildingName": "东区1号楼",
"campusName": "东校区",
"areaName": "东区宿舍",
"floors": 6
}
]
return { total: 0, rows: [] };
}
```
@@ -54,6 +54,7 @@ create table `xg_dorm_building` (
`building_name` varchar(100) not null comment '宿舍楼名称',
`area_id` bigint(20) not null comment '所属宿舍区ID',
`campus_id` bigint(20) not null comment '所属校区ID',
`floors` int(11) not null default '6' comment '总楼层数',
`building_type` char(1) default '1' comment '宿舍楼类型(1男生楼 2女生楼 3混合楼)',
`building_description` varchar(500) default null comment '宿舍楼描述',
`sort_order` int(11) default '0' comment '排序号',
@@ -14,6 +14,7 @@ export interface DormBuildingVO {
remark: string;
areaName?: string;
campusName?: string;
floors: number;
}
export interface DormBuildingForm extends Omit<DormBuildingVO, 'createTime' | 'createBy' | 'updateTime' | 'updateBy' | 'areaName' | 'campusName'> {
@@ -25,6 +26,7 @@ export interface DormBuildingQuery {
areaId?: string | number;
campusId?: string | number;
buildingType?: string;
floors?: number;
params?: Record<string, any>;
}
@@ -40,6 +42,7 @@ export interface BuildingTreeSelectVo {
createBy?: number;
createTime?: string;
updateBy?: number;
floors?: number;
updateTime?: string;
}
+2 -2
View File
@@ -5,11 +5,11 @@ import { DormQuery, RoomForm, BedForm, DormTreeNode, RoomInfo, BedInfo } from '.
const baseUrl = '/xsssfjgl';
/**
* 获取宿舍树形结构
* 获取宿舍楼树形结构
*/
export function getDormTree() {
return request({
url: baseUrl + '/tree',
url: '/xgss/dormBuildingTree/list',
method: 'get'
});
}
@@ -86,6 +86,7 @@
<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">
<dict-tag :options="dorm_type" :value="scope.row.buildingType" />
@@ -128,7 +129,18 @@
<el-input v-model="form.buildingCode" placeholder="请输入宿舍楼编码" />
</el-form-item>
<el-form-item label="宿舍楼名称" prop="buildingName">
<el-input v-model="form.buildingName" placeholder="请输入宿舍楼名称" />
<el-input v-model="form.buildingName" placeholder="请输入宿舍楼名称" class="fixed-width-input" />
</el-form-item>
<el-form-item label="总楼层数" prop="floors">
<el-input-number
v-model="form.floors"
:min="1"
:max="99"
:step="1"
:default-value="6"
placeholder="请输入总楼层数"
class="fixed-width-input"
/>
</el-form-item>
<el-form-item label="宿舍楼类型" prop="buildingType">
<el-select v-model="form.buildingType" placeholder="请选择宿舍楼类型">
@@ -198,7 +210,8 @@ const initFormData: DormBuildingForm = {
campusId: undefined,
buildingType: undefined,
buildingDescription: undefined,
sortOrder: undefined
sortOrder: undefined,
floors: 6 // 添加默认值为6
};
const data = reactive<PageData<DormBuildingForm, DormBuildingQuery>>({
form: { ...initFormData },
@@ -537,4 +550,7 @@ onMounted(() => {
margin-bottom: 0 !important;
}
}
.fixed-width-input {
width: 100%;
}
</style>
+56 -36
View File
@@ -1,10 +1,18 @@
<template>
<div class="p-2">
<el-row :gutter="20">
<div class="flex-layout">
<!-- 宿舍树 -->
<el-col :lg="4" :xs="24">
<div class="tree-sidebar">
<el-card shadow="hover">
<el-input v-model="buildingName" placeholder="请输入宿舍楼名称" prefix-icon="Search" clearable />
<el-input
v-model="buildingName"
prefix-icon="Search"
placeholder="请输入宿舍楼名称"
clearable
@clear="watchBuildingName"
@keyup.enter="watchBuildingName"
>
</el-input>
<el-tree
ref="dormTreeRef"
class="mt-2"
@@ -18,9 +26,9 @@
@node-click="handleNodeClick"
/>
</el-card>
</el-col>
</div>
<el-col :lg="20" :xs="24">
<div class="content-area">
<transition :enter-active-class="animate.searchAnimate.enter" :leave-active-class="animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
@@ -109,8 +117,8 @@
</div>
</el-card>
</template>
</el-col>
</el-row>
</div>
</div>
<!-- 房间管理对话框 -->
<room-dialog
@@ -159,21 +167,11 @@
</template>
<script setup name="DormRoomManagement" lang="ts">
import { ref, computed, reactive, onMounted, nextTick } from 'vue';
import { ref, computed, reactive, onMounted, nextTick, watch } from 'vue';
import { ElMessageBox, ElMessage } from 'element-plus';
import { InfoFilled } from '@element-plus/icons-vue';
import { InfoFilled, Search } from '@element-plus/icons-vue';
import { DormTreeNode, RoomInfo, BedInfo, DormQuery } from '@/api/xgss/xsssfjgl/types';
import {
getDormTree,
listRooms,
delRoom,
listBeds,
delBed,
// 以下为模拟数据
getMockDormTree,
getMockRooms,
getMockBeds
} from '@/api/xgss/xsssfjgl';
import { getDormTree, listRooms, delRoom, listBeds, delBed } from '@/api/xgss/xsssfjgl';
// 引入子组件
import RoomDialog from './components/RoomDialog.vue';
@@ -242,7 +240,7 @@ const bedLoading = ref(false);
/** 通过条件过滤节点 */
const filterNode = (value: string, data: DormTreeNode) => {
if (!value) return true;
return data.label.indexOf(value) !== -1;
return data.label.toLowerCase().indexOf(value.toLowerCase()) !== -1;
};
/** 监听宿舍楼名称变化,筛选树 */
@@ -250,12 +248,15 @@ const watchBuildingName = () => {
dormTreeRef.value?.filter(buildingName.value);
};
// 监听buildingName变化,实时筛选宿舍楼
watch(buildingName, () => {
watchBuildingName();
});
/** 获取宿舍树数据 */
const getDormTreeData = async () => {
try {
// const res = await getDormTree();
// 使用模拟数据
const res = getMockDormTree();
const res = await getDormTree();
dormOptions.value = res.data;
} catch (error) {
console.error('获取宿舍树失败', error);
@@ -279,9 +280,7 @@ const loadRoomData = async () => {
if (!queryParams.buildingId) return;
try {
// const res = await listRooms(queryParams);
// 使用模拟数据
const res = getMockRooms(queryParams.buildingId);
const res = await listRooms(queryParams);
roomList.value = res.rows;
} catch (error) {
console.error('获取房间列表失败', error);
@@ -366,9 +365,7 @@ const handleManageBeds = async (room: RoomInfo) => {
const loadBedData = async (roomId: string | number) => {
bedLoading.value = true;
try {
// const res = await listBeds(roomId);
// 使用模拟数据
const res = getMockBeds(roomId);
const res = await listBeds(roomId);
bedList.value = res.data;
} catch (error) {
console.error('获取床位列表失败', error);
@@ -420,16 +417,39 @@ const handleBedSuccess = () => {
onMounted(() => {
getDormTreeData();
// 监听宿舍楼名称变化
nextTick(() => {
buildingName.value = '';
watchBuildingName();
});
});
// 确保树加载后才能过滤
watch(
dormOptions,
() => {
if (buildingName.value) {
nextTick(() => {
watchBuildingName();
});
}
},
{ deep: true }
);
</script>
<style scoped>
.flex-layout {
display: flex;
gap: 20px;
}
.tree-sidebar {
min-width: 320px;
max-width: 320px;
flex-shrink: 0;
}
.content-area {
flex: 1;
min-width: 0; /* 防止内容溢出 */
}
.room-container {
width: 100%;
}
+2 -9
View File
@@ -418,15 +418,8 @@ function onScope(type: string, admin: any) {
scopeDialogVisible.value = true;
}
function onDialogSubmit(admin: any) {
// TODO: 新增/编辑时调用API,成功后刷新
const list = getListByType(activeTab.value);
if (dialogMode.value === 'add') {
admin.id = Date.now();
list.value.push(admin);
} else {
const idx = list.value.findIndex((a: any) => a.id === admin.id);
if (idx !== -1) list.value[idx] = { ...admin };
}
// 修改后立即刷新列表数据
fetchAdmins(activeTab.value);
dialogVisible.value = false;
}
async function onScopeSubmit(scope: any) {