wuzhen пре 1 година
родитељ
комит
3702472b84
35 измењених фајлова са 4636 додато и 347 уклоњено
  1. 132 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentManagerController.java
  2. 141 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentManagerSourceController.java
  3. 77 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentManager.java
  4. 67 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentManagerSource.java
  5. 77 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentManagerBo.java
  6. 67 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentManagerSourceBo.java
  7. 15 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/ClassTeacherInfoVo.java
  8. 56 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentManagerDeptTreeVo.java
  9. 80 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentManagerSourceVo.java
  10. 111 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentManagerVo.java
  11. 25 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgStudentManagerMapper.java
  12. 16 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgStudentManagerSourceMapper.java
  13. 86 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgStudentManagerService.java
  14. 92 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgStudentManagerSourceService.java
  15. 1 1
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormManagerServiceImpl.java
  16. 186 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentManagerServiceImpl.java
  17. 353 0
      backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentManagerSourceServiceImpl.java
  18. 36 0
      backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/xgStudentManager/XgStudentManagerMapper.xml
  19. 7 0
      backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/xgStudentManagerSource/XgStudentManagerSourceMapper.xml
  20. 17 0
      docs/变更管理/code-adjust-record-2025-07-24-wuzilong.md
  21. 436 22
      docs/变更管理/code-adjust-record-2025-07-24-zlwu.md
  22. 49 0
      docs/开发文档/API/API文档总览.md
  23. 357 0
      docs/开发文档/API/学生管理员授权关系API.md
  24. 259 0
      docs/开发文档/API/宿舍管理员信息API.md
  25. 10 5
      web/src/api/studentMsg/index.ts
  26. 97 0
      web/src/api/studentMsg/types.ts
  27. 95 0
      web/src/api/xgStudentManager/studentManager/index.ts
  28. 156 0
      web/src/api/xgStudentManager/studentManager/types.ts
  29. 63 0
      web/src/api/xgStudentManagerSource/studentManagerSource/index.ts
  30. 126 0
      web/src/api/xgStudentManagerSource/studentManagerSource/types.ts
  31. 178 24
      web/src/views/studentMsg/authmsg/components/AdminDialog.vue
  32. 161 55
      web/src/views/studentMsg/authmsg/components/ScopeDialog.vue
  33. 140 87
      web/src/views/studentMsg/authmsg/index.vue
  34. 810 146
      web/src/views/studentMsg/studentMsg/index.vue
  35. 57 7
      web/src/views/system/user/index.vue

+ 132 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentManagerController.java

@@ -0,0 +1,132 @@
+package org.dromara.xg.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+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.XgStudentManagerVo;
+import org.dromara.xg.domain.bo.XgStudentManagerBo;
+import org.dromara.xg.service.IXgStudentManagerService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.xg.domain.vo.ClassTeacherInfoVo;
+
+/**
+ * 宿舍管理员信息
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/xgStudentManager/studentManager")
+public class XgStudentManagerController extends BaseController {
+
+    private final IXgStudentManagerService xgStudentManagerService;
+
+    /**
+     * 查询宿舍管理员信息列表
+     */
+    @SaCheckPermission("xgStudentManager:studentManager:list")
+    @GetMapping("/list")
+    public TableDataInfo<XgStudentManagerVo> list(XgStudentManagerBo bo, PageQuery pageQuery) {
+        return xgStudentManagerService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出宿舍管理员信息列表
+     */
+    @SaCheckPermission("xgStudentManager:studentManager:export")
+    @Log(title = "宿舍管理员信息", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(XgStudentManagerBo bo, HttpServletResponse response) {
+        List<XgStudentManagerVo> list = xgStudentManagerService.queryList(bo);
+        ExcelUtil.exportExcel(list, "宿舍管理员信息", XgStudentManagerVo.class, response);
+    }
+
+    /**
+     * 获取宿舍管理员信息详细信息
+     *
+     * @param id 主键
+     */
+    @SaCheckPermission("xgStudentManager:studentManager:query")
+    @GetMapping("/{id}")
+    public R<XgStudentManagerVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
+        return R.ok(xgStudentManagerService.queryById(id));
+    }
+
+    /**
+     * 新增宿舍管理员信息
+     */
+    @SaCheckPermission("xgStudentManager:studentManager:add")
+    @Log(title = "宿舍管理员信息", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody XgStudentManagerBo bo) {
+        return toAjax(xgStudentManagerService.insertByBo(bo));
+    }
+
+    /**
+     * 修改宿舍管理员信息
+     */
+    @SaCheckPermission("xgStudentManager:studentManager:edit")
+    @Log(title = "宿舍管理员信息", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody XgStudentManagerBo bo) {
+        return toAjax(xgStudentManagerService.updateByBo(bo));
+    }
+
+    /**
+     * 删除宿舍管理员信息
+     *
+     * @param ids 主键串
+     */
+    @SaCheckPermission("xgStudentManager:studentManager:remove")
+    @Log(title = "宿舍管理员信息", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
+        return toAjax(xgStudentManagerService.deleteWithValidByIds(List.of(ids), true));
+    }
+
+    /**
+     * 获取可用的教职工信息列表
+     *
+     * @param managerType 管理员类型(1 全校管理员 2 自定义管理员 3 班主任)
+     */
+    // @SaCheckPermission("xgStudentManager:studentManager:list")
+    @GetMapping("/listAvailableTeachers")
+    public R<List<XgStudentManagerVo>> listAvailableTeachers(@RequestParam("managerType") String managerType) {
+        List<XgStudentManagerVo> teachers = xgStudentManagerService.queryAvailableTeachers(managerType);
+        return R.ok(teachers);
+    }
+
+    /**
+     * 获取当前正在教学的班主任信息
+     *
+     * @param phone 手机号码(可选,模糊查询)
+     * @param name  姓名(可选,模糊查询)
+     * @return 班主任信息列表
+     */
+    @SaCheckPermission("xgStudentManager:studentManager:list")
+    @GetMapping("/listActiveClassTeachers")
+    public R<List<ClassTeacherInfoVo>> listActiveClassTeachers(
+            @RequestParam(value = "phone", required = false) String phone,
+            @RequestParam(value = "name", required = false) String name) {
+        List<ClassTeacherInfoVo> teachers = xgStudentManagerService.queryActiveClassTeachers(phone, name);
+        return R.ok(teachers);
+    }
+}

+ 141 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentManagerSourceController.java

@@ -0,0 +1,141 @@
+package org.dromara.xg.controller;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.constraints.*;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.validation.annotation.Validated;
+import org.dromara.common.idempotent.annotation.RepeatSubmit;
+import org.dromara.common.log.annotation.Log;
+import org.dromara.common.web.core.BaseController;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.common.core.domain.R;
+import org.dromara.common.core.validate.AddGroup;
+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.XgStudentManagerSourceVo;
+import org.dromara.xg.domain.vo.XgStudentManagerDeptTreeVo;
+import org.dromara.xg.domain.bo.XgStudentManagerSourceBo;
+import org.dromara.xg.service.IXgStudentManagerSourceService;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+
+/**
+ * 学生管理员授权关系
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+@Validated
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/xgStudentManagerSource/studentManagerSource")
+public class XgStudentManagerSourceController extends BaseController {
+
+    private final IXgStudentManagerSourceService xgStudentManagerSourceService;
+
+    /**
+     * 查询学生管理员授权关系列表
+     */
+    @SaCheckPermission("xgStudentManagerSource:studentManagerSource:list")
+    @GetMapping("/list")
+    public TableDataInfo<XgStudentManagerSourceVo> list(XgStudentManagerSourceBo bo, PageQuery pageQuery) {
+        return xgStudentManagerSourceService.queryPageList(bo, pageQuery);
+    }
+
+    /**
+     * 导出学生管理员授权关系列表
+     */
+    @SaCheckPermission("xgStudentManagerSource:studentManagerSource:export")
+    @Log(title = "学生管理员授权关系", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(XgStudentManagerSourceBo bo, HttpServletResponse response) {
+        List<XgStudentManagerSourceVo> list = xgStudentManagerSourceService.queryList(bo);
+        ExcelUtil.exportExcel(list, "学生管理员授权关系", XgStudentManagerSourceVo.class, response);
+    }
+
+    /**
+     * 获取学生管理员授权关系详细信息
+     *
+     * @param id 主键
+     */
+    @SaCheckPermission("xgStudentManagerSource:studentManagerSource:query")
+    @GetMapping("/{id}")
+    public R<XgStudentManagerSourceVo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
+        return R.ok(xgStudentManagerSourceService.queryById(id));
+    }
+
+    /**
+     * 新增学生管理员授权关系
+     */
+    @SaCheckPermission("xgStudentManagerSource:studentManagerSource:add")
+    @Log(title = "学生管理员授权关系", businessType = BusinessType.INSERT)
+    @RepeatSubmit()
+    @PostMapping()
+    public R<Void> add(@Validated(AddGroup.class) @RequestBody XgStudentManagerSourceBo bo) {
+        return toAjax(xgStudentManagerSourceService.insertByBo(bo));
+    }
+
+    /**
+     * 修改学生管理员授权关系
+     */
+    @SaCheckPermission("xgStudentManagerSource:studentManagerSource:edit")
+    @Log(title = "学生管理员授权关系", businessType = BusinessType.UPDATE)
+    @RepeatSubmit()
+    @PutMapping()
+    public R<Void> edit(@Validated(EditGroup.class) @RequestBody XgStudentManagerSourceBo bo) {
+        return toAjax(xgStudentManagerSourceService.updateByBo(bo));
+    }
+
+    /**
+     * 删除学生管理员授权关系
+     *
+     * @param ids 主键串
+     */
+    @SaCheckPermission("xgStudentManagerSource:studentManagerSource:remove")
+    @Log(title = "学生管理员授权关系", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
+        return toAjax(xgStudentManagerSourceService.deleteWithValidByIds(List.of(ids), true));
+    }
+
+    /**
+     * 自定义学生管理员授予部门权限
+     *
+     * @param managerId     管理员ID
+     * @param authSourceIds 资源ID列表
+     */
+    @SaCheckPermission("xgStudentManagerSource:studentManagerSource:grantDeptAuth")
+    @Log(title = "学生管理员授权关系", businessType = BusinessType.UPDATE)
+    @PostMapping("/grantDeptAuth")
+    public R<Void> grantDeptAuth(@RequestParam Long managerId, @RequestBody List<Long> authSourceIds) {
+        xgStudentManagerSourceService.grantDeptAuth(managerId, authSourceIds);
+        return R.ok();
+    }
+
+    /**
+     * 获取指定管理员的授权资源名称列表
+     *
+     * @param managerId 管理员ID
+     * @return 授权资源名称数组
+     */
+    @SaCheckPermission("xgStudentManagerSource:studentManagerSource:query")
+    @GetMapping("/authSourceNames")
+    public R<List<String>> getAuthSourceNames(@RequestParam Long managerId) {
+        return R.ok(xgStudentManagerSourceService.getAuthSourceNames(managerId));
+    }
+
+    /**
+     * 获取当前用户的学生管理权限部门树
+     *
+     * @return 部门树形结构列表
+     */
+    @SaCheckPermission("xgStudentManagerSource:studentManagerSource:query")
+    @GetMapping("/currentUserAuthTree")
+    public R<List<XgStudentManagerDeptTreeVo>> getCurrentUserAuthTree() {
+        return R.ok(xgStudentManagerSourceService.getCurrentUserAuthDeptTree());
+    }
+}

+ 77 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentManager.java

@@ -0,0 +1,77 @@
+package org.dromara.xg.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 宿舍管理员信息对象 xg_student_manager
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("xg_student_manager")
+public class XgStudentManager extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 管理员姓名
+     */
+    private String managerName;
+
+    /**
+     * 管理员类型(1 全校管理员 2 自定义管理员 3 班主任)
+     */
+    private String managerType;
+
+    /**
+     * 管理级别(1-3,1为最高)
+     */
+    private Long managerLevel;
+
+    /**
+     * 关联用户ID
+     */
+    private Long userId;
+
+    /**
+     * 联系电话
+     */
+    private String phone;
+
+    /**
+     * 性别(0男 1女 2未知)
+     */
+    private String gender;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    @TableLogic(value = "0", delval = "1")
+    private String deleted;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 67 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/XgStudentManagerSource.java

@@ -0,0 +1,67 @@
+package org.dromara.xg.domain;
+
+import org.dromara.common.tenant.core.TenantEntity;
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.io.Serial;
+
+/**
+ * 学生管理员授权关系对象 xg_student_manager_source
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("xg_student_manager_source")
+public class XgStudentManagerSource extends TenantEntity {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * 管理员ID
+     */
+    private Long managerId;
+
+    /**
+     * 权限类型(1查看 2编辑 3完全管理)
+     */
+    private String permissionType;
+
+    /**
+     * 授权类型(1全校级 2自定义 3班主任)
+     */
+    private String authType;
+
+    /**
+     * 授权来源ID(对应班级id)
+     */
+    private Long authSource;
+
+    /**
+     * 状态(0有效 1已撤销)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    @TableLogic(value = "0", delval = "1")
+    private String deleted;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 77 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentManagerBo.java

@@ -0,0 +1,77 @@
+package org.dromara.xg.domain.bo;
+
+import org.dromara.xg.domain.XgStudentManager;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+/**
+ * 宿舍管理员信息业务对象 xg_student_manager
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = XgStudentManager.class, reverseConvertGenerate = false)
+public class XgStudentManagerBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    @NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 管理员姓名
+     */
+    @NotBlank(message = "管理员姓名不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String managerName;
+
+    /**
+     * 管理员类型(1 全校管理员 2 自定义管理员 3 班主任)
+     */
+    @NotBlank(message = "管理员类型(1 全校管理员 2 自定义管理员 3 班主任)不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String managerType;
+
+    /**
+     * 管理级别(1-3,1为最高)
+     */
+    private Long managerLevel;
+
+    /**
+     * 关联用户ID
+     */
+    private Long userId;
+
+    /**
+     * 联系电话
+     */
+    private String phone;
+
+    /**
+     * 性别(0男 1女 2未知)
+     */
+    private String gender;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    private String deleted;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 67 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentManagerSourceBo.java

@@ -0,0 +1,67 @@
+package org.dromara.xg.domain.bo;
+
+import org.dromara.xg.domain.XgStudentManagerSource;
+import org.dromara.common.mybatis.core.domain.BaseEntity;
+import org.dromara.common.core.validate.AddGroup;
+import org.dromara.common.core.validate.EditGroup;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import jakarta.validation.constraints.*;
+
+/**
+ * 学生管理员授权关系业务对象 xg_student_manager_source
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@AutoMapper(target = XgStudentManagerSource.class, reverseConvertGenerate = false)
+public class XgStudentManagerSourceBo extends BaseEntity {
+
+    /**
+     * 主键ID
+     */
+    @NotNull(message = "主键ID不能为空", groups = { EditGroup.class })
+    private Long id;
+
+    /**
+     * 管理员ID
+     */
+    @NotNull(message = "管理员ID不能为空", groups = { AddGroup.class, EditGroup.class })
+    private Long managerId;
+
+    /**
+     * 权限类型(1查看 2编辑 3完全管理)
+     */
+    private String permissionType;
+
+    /**
+     * 授权类型(1全校级 2自定义 3班主任)
+     */
+    @NotBlank(message = "授权类型(1全校级 2自定义 3班主任)不能为空", groups = { AddGroup.class, EditGroup.class })
+    private String authType;
+
+    /**
+     * 授权来源ID(对应班级id)
+     */
+    private Long authSource;
+
+    /**
+     * 状态(0有效 1已撤销)
+     */
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    private String deleted;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+
+}

+ 15 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/ClassTeacherInfoVo.java

@@ -0,0 +1,15 @@
+package org.dromara.xg.domain.vo;
+
+import lombok.Data;
+import java.io.Serializable;
+
+/**
+ * 班主任信息VO
+ */
+@Data
+public class ClassTeacherInfoVo implements Serializable {
+    private String name;
+    private String phone;
+    private String sex;
+    private String className;
+}

+ 56 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentManagerDeptTreeVo.java

@@ -0,0 +1,56 @@
+package org.dromara.xg.domain.vo;
+
+import lombok.Data;
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 学生管理权限部门树视图对象
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+@Data
+public class XgStudentManagerDeptTreeVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 部门id (前端期望字段名)
+     */
+    private Long id;
+
+    /**
+     * 部门名称 (前端期望字段名)
+     */
+    private String label;
+
+    /**
+     * 父部门id
+     */
+    private Long parentId;
+
+    /**
+     * 部门类别编码
+     */
+    private String deptCategory;
+
+    /**
+     * 显示顺序
+     */
+    private Integer weight;
+
+    /**
+     * 是否禁用(与clickable相反,前端期望字段名)
+     */
+    private Boolean disabled;
+
+    /**
+     * 子部门
+     */
+    private List<XgStudentManagerDeptTreeVo> children = new ArrayList<>();
+
+}

+ 80 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentManagerSourceVo.java

@@ -0,0 +1,80 @@
+package org.dromara.xg.domain.vo;
+
+import org.dromara.xg.domain.XgStudentManagerSource;
+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 java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+
+/**
+ * 学生管理员授权关系视图对象 xg_student_manager_source
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = XgStudentManagerSource.class)
+public class XgStudentManagerSourceVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @ExcelProperty(value = "主键ID")
+    private Long id;
+
+    /**
+     * 管理员ID
+     */
+    @ExcelProperty(value = "管理员ID")
+    private Long managerId;
+
+    /**
+     * 权限类型(1查看 2编辑 3完全管理)
+     */
+    @ExcelProperty(value = "权限类型(1查看 2编辑 3完全管理)")
+    private String permissionType;
+
+    /**
+     * 授权类型(1全校级 2自定义 3班主任)
+     */
+    @ExcelProperty(value = "授权类型(1全校级 2自定义 3班主任)")
+    private String authType;
+
+    /**
+     * 授权来源ID(对应班级id)
+     */
+    @ExcelProperty(value = "授权来源ID(对应班级id)")
+    private Long authSource;
+
+    /**
+     * 状态(0有效 1已撤销)
+     */
+    @ExcelProperty(value = "状态(0有效 1已撤销)")
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    @ExcelProperty(value = "删除标志(0代表存在 1代表删除)")
+    private String deleted;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    private String remark;
+
+
+}

+ 111 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentManagerVo.java

@@ -0,0 +1,111 @@
+package org.dromara.xg.domain.vo;
+
+import org.dromara.xg.domain.XgStudentManager;
+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 com.fasterxml.jackson.annotation.JsonFormat;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 宿舍管理员信息视图对象 xg_student_manager
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = XgStudentManager.class)
+public class XgStudentManagerVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @ExcelProperty(value = "主键ID")
+    private Long id;
+
+    /**
+     * 管理员姓名
+     */
+    @ExcelProperty(value = "管理员姓名")
+    private String managerName;
+
+    /**
+     * 管理员类型(1 全校管理员 2 自定义管理员 3 班主任)
+     */
+    @ExcelProperty(value = "管理员类型(1 全校管理员 2 自定义管理员 3 班主任)")
+    private String managerType;
+
+    /**
+     * 管理级别(1-3,1为最高)
+     */
+    @ExcelProperty(value = "管理级别(1-3,1为最高)")
+    private Long managerLevel;
+
+    /**
+     * 关联用户ID
+     */
+    @ExcelProperty(value = "关联用户ID")
+    private Long userId;
+
+    /**
+     * 联系电话
+     */
+    @ExcelProperty(value = "联系电话")
+    private String phone;
+
+    /**
+     * 性别(0男 1女 2未知)
+     */
+    @ExcelProperty(value = "性别(0男 1女 2未知)")
+    private String gender;
+
+    /**
+     * 状态(0正常 1停用)
+     */
+    @ExcelProperty(value = "状态(0正常 1停用)")
+    private String status;
+
+    /**
+     * 删除标志(0代表存在 1代表删除)
+     */
+    @ExcelProperty(value = "删除标志(0代表存在 1代表删除)")
+    private String deleted;
+
+    /**
+     * 备注
+     */
+    @ExcelProperty(value = "备注")
+    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;
+}

+ 25 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgStudentManagerMapper.java

@@ -0,0 +1,25 @@
+package org.dromara.xg.mapper;
+
+import org.dromara.xg.domain.XgStudentManager;
+import org.dromara.xg.domain.vo.XgStudentManagerVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 宿舍管理员信息Mapper接口
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+public interface XgStudentManagerMapper extends BaseMapperPlus<XgStudentManager, XgStudentManagerVo> {
+
+    /**
+     * 查询可用的教职工信息
+     *
+     * @param managerType 管理员类型
+     * @return 可用的教职工信息列表
+     */
+    List<XgStudentManagerVo> selectAvailableTeachers(@Param("managerType") String managerType);
+}

+ 16 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgStudentManagerSourceMapper.java

@@ -0,0 +1,16 @@
+package org.dromara.xg.mapper;
+
+import org.dromara.xg.domain.XgStudentManagerSource;
+import org.dromara.xg.domain.vo.XgStudentManagerSourceVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 学生管理员授权关系Mapper接口
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+public interface XgStudentManagerSourceMapper extends BaseMapperPlus<XgStudentManagerSource, XgStudentManagerSourceVo> {
+
+    // 预留自定义SQL方法接口
+}

+ 86 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgStudentManagerService.java

@@ -0,0 +1,86 @@
+package org.dromara.xg.service;
+
+import org.dromara.xg.domain.vo.XgStudentManagerVo;
+import org.dromara.xg.domain.bo.XgStudentManagerBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import org.dromara.xg.domain.vo.ClassTeacherInfoVo;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 宿舍管理员信息Service接口
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+public interface IXgStudentManagerService {
+
+    /**
+     * 查询宿舍管理员信息
+     *
+     * @param id 主键
+     * @return 宿舍管理员信息
+     */
+    XgStudentManagerVo queryById(Long id);
+
+    /**
+     * 分页查询宿舍管理员信息列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 宿舍管理员信息分页列表
+     */
+    TableDataInfo<XgStudentManagerVo> queryPageList(XgStudentManagerBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的宿舍管理员信息列表
+     *
+     * @param bo 查询条件
+     * @return 宿舍管理员信息列表
+     */
+    List<XgStudentManagerVo> queryList(XgStudentManagerBo bo);
+
+    /**
+     * 新增宿舍管理员信息
+     *
+     * @param bo 宿舍管理员信息
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(XgStudentManagerBo bo);
+
+    /**
+     * 修改宿舍管理员信息
+     *
+     * @param bo 宿舍管理员信息
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(XgStudentManagerBo bo);
+
+    /**
+     * 校验并批量删除宿舍管理员信息信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 获取可用的教职工信息列表
+     *
+     * @param managerType 管理员类型(1 全校管理员 2 自定义管理员 3 班主任)
+     * @return 可用的教职工信息列表
+     */
+    List<XgStudentManagerVo> queryAvailableTeachers(String managerType);
+
+    /**
+     * 获取当前正在教学的班主任信息
+     *
+     * @param phone 手机号
+     * @param name  姓名
+     * @return 班主任信息列表
+     */
+    List<ClassTeacherInfoVo> queryActiveClassTeachers(String phone, String name);
+}

+ 92 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgStudentManagerSourceService.java

@@ -0,0 +1,92 @@
+package org.dromara.xg.service;
+
+import org.dromara.xg.domain.vo.XgStudentManagerSourceVo;
+import org.dromara.xg.domain.vo.XgStudentManagerDeptTreeVo;
+import org.dromara.xg.domain.bo.XgStudentManagerSourceBo;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 学生管理员授权关系Service接口
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+public interface IXgStudentManagerSourceService {
+
+    /**
+     * 查询学生管理员授权关系
+     *
+     * @param id 主键
+     * @return 学生管理员授权关系
+     */
+    XgStudentManagerSourceVo queryById(Long id);
+
+    /**
+     * 分页查询学生管理员授权关系列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 学生管理员授权关系分页列表
+     */
+    TableDataInfo<XgStudentManagerSourceVo> queryPageList(XgStudentManagerSourceBo bo, PageQuery pageQuery);
+
+    /**
+     * 查询符合条件的学生管理员授权关系列表
+     *
+     * @param bo 查询条件
+     * @return 学生管理员授权关系列表
+     */
+    List<XgStudentManagerSourceVo> queryList(XgStudentManagerSourceBo bo);
+
+    /**
+     * 新增学生管理员授权关系
+     *
+     * @param bo 学生管理员授权关系
+     * @return 是否新增成功
+     */
+    Boolean insertByBo(XgStudentManagerSourceBo bo);
+
+    /**
+     * 修改学生管理员授权关系
+     *
+     * @param bo 学生管理员授权关系
+     * @return 是否修改成功
+     */
+    Boolean updateByBo(XgStudentManagerSourceBo bo);
+
+    /**
+     * 校验并批量删除学生管理员授权关系信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
+
+    /**
+     * 自定义学生管理员授予部门权限
+     *
+     * @param managerId     管理员ID
+     * @param authSourceIds 资源ID列表
+     */
+    void grantDeptAuth(Long managerId, List<Long> authSourceIds);
+
+    /**
+     * 获取指定管理员的授权资源名称列表
+     *
+     * @param managerId 管理员ID
+     * @return 授权资源名称数组
+     */
+    List<String> getAuthSourceNames(Long managerId);
+
+    /**
+     * 获取当前用户的学生管理权限部门树
+     *
+     * @return 部门树形结构列表
+     */
+    List<XgStudentManagerDeptTreeVo> getCurrentUserAuthDeptTree();
+}

+ 1 - 1
backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormManagerServiceImpl.java

@@ -84,7 +84,7 @@ public class XgDormManagerServiceImpl implements IXgDormManagerService {
     public List<Map<String, Object>> queryTeacherList(String name, String managerType) {
         StringBuilder sql = new StringBuilder();
         sql.append(
-                "SELECT u.user_id as value, u.nick_name as label, u.user_name as username,u.phonenumber as phonenumber, u.sex as sex ");
+                "SELECT distinct u.user_id as value, u.nick_name as label, u.user_name as username,u.phonenumber as phonenumber, u.sex as sex ");
         sql.append("FROM sys_user u ");
         sql.append("INNER JOIN sys_user_role ur ON u.user_id = ur.user_id ");
         sql.append("INNER JOIN sys_role r ON ur.role_id = r.role_id ");

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

@@ -0,0 +1,186 @@
+package org.dromara.xg.service.impl;
+
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.dromara.xg.domain.bo.XgStudentManagerBo;
+import org.dromara.xg.domain.vo.XgStudentManagerVo;
+import org.dromara.xg.domain.XgStudentManager;
+import org.dromara.xg.mapper.XgStudentManagerMapper;
+import org.dromara.xg.service.IXgStudentManagerService;
+import org.dromara.xg.domain.vo.ClassTeacherInfoVo;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+/**
+ * 宿舍管理员信息Service业务层处理
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class XgStudentManagerServiceImpl implements IXgStudentManagerService {
+
+    private final XgStudentManagerMapper baseMapper;
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    /**
+     * 查询宿舍管理员信息
+     *
+     * @param id 主键
+     * @return 宿舍管理员信息
+     */
+    @Override
+    public XgStudentManagerVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询宿舍管理员信息列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 宿舍管理员信息分页列表
+     */
+    @Override
+    public TableDataInfo<XgStudentManagerVo> queryPageList(XgStudentManagerBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<XgStudentManager> lqw = buildQueryWrapper(bo);
+        Page<XgStudentManagerVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的宿舍管理员信息列表
+     *
+     * @param bo 查询条件
+     * @return 宿舍管理员信息列表
+     */
+    @Override
+    public List<XgStudentManagerVo> queryList(XgStudentManagerBo bo) {
+        LambdaQueryWrapper<XgStudentManager> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<XgStudentManager> buildQueryWrapper(XgStudentManagerBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<XgStudentManager> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(XgStudentManager::getId);
+        lqw.like(StringUtils.isNotBlank(bo.getManagerName()), XgStudentManager::getManagerName, bo.getManagerName());
+        lqw.eq(StringUtils.isNotBlank(bo.getManagerType()), XgStudentManager::getManagerType, bo.getManagerType());
+        lqw.eq(bo.getManagerLevel() != null, XgStudentManager::getManagerLevel, bo.getManagerLevel());
+        lqw.eq(bo.getUserId() != null, XgStudentManager::getUserId, bo.getUserId());
+        lqw.like(StringUtils.isNotBlank(bo.getPhone()), XgStudentManager::getPhone, bo.getPhone());
+        lqw.eq(StringUtils.isNotBlank(bo.getGender()), XgStudentManager::getGender, bo.getGender());
+        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), XgStudentManager::getStatus, bo.getStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getDeleted()), XgStudentManager::getDeleted, bo.getDeleted());
+        return lqw;
+    }
+
+    /**
+     * 新增宿舍管理员信息
+     *
+     * @param bo 宿舍管理员信息
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(XgStudentManagerBo bo) {
+        XgStudentManager add = MapstructUtils.convert(bo, XgStudentManager.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改宿舍管理员信息
+     *
+     * @param bo 宿舍管理员信息
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(XgStudentManagerBo bo) {
+        XgStudentManager update = MapstructUtils.convert(bo, XgStudentManager.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(XgStudentManager entity) {
+        // TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除宿舍管理员信息信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            // TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+
+    /**
+     * 获取可用的教职工信息列表
+     *
+     * @param managerType 管理员类型(1 全校管理员 2 自定义管理员 3 班主任)
+     * @return 可用的教职工信息列表
+     */
+    @Override
+    public List<XgStudentManagerVo> queryAvailableTeachers(String managerType) {
+        return baseMapper.selectAvailableTeachers(managerType);
+    }
+
+    @Override
+    public List<ClassTeacherInfoVo> queryActiveClassTeachers(String phone, String name) {
+        StringBuilder sql = new StringBuilder(
+                "SELECT s.nick_name as name, s.phonenumber as phone, s.sex as sex, c.class_name as className " +
+                        "FROM xg_class_msg c, sys_user s " +
+                        "WHERE c.deleted = '0' " +
+                        "AND c.class_status = '1' " +
+                        "AND c.class_teacher_id IS NOT NULL " +
+                        "AND c.class_teacher_id = s.user_id " +
+                        "AND s.del_flag = '0' " +
+                        "AND s.status = '0'");
+        List<Object> params = new java.util.ArrayList<>();
+        if (StringUtils.isNotBlank(name)) {
+            sql.append(" AND s.nick_name LIKE ?");
+            params.add("%" + name + "%");
+        }
+        if (StringUtils.isNotBlank(phone)) {
+            sql.append(" AND s.phonenumber LIKE ?");
+            params.add("%" + phone + "%");
+        }
+        return jdbcTemplate.query(sql.toString(), params.toArray(), (rs, rowNum) -> {
+            ClassTeacherInfoVo vo = new ClassTeacherInfoVo();
+            vo.setName(rs.getString("name"));
+            vo.setPhone(rs.getString("phone"));
+            vo.setSex(rs.getString("sex"));
+            vo.setClassName(rs.getString("className"));
+            return vo;
+        });
+    }
+}

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

@@ -0,0 +1,353 @@
+package org.dromara.xg.service.impl;
+
+import org.dromara.common.core.utils.MapstructUtils;
+import org.dromara.common.core.utils.StringUtils;
+import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.common.mybatis.core.page.PageQuery;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.dromara.xg.domain.bo.XgStudentManagerSourceBo;
+import org.dromara.xg.domain.vo.XgStudentManagerSourceVo;
+import org.dromara.xg.domain.vo.XgStudentManagerDeptTreeVo;
+import org.dromara.xg.domain.XgStudentManagerSource;
+import org.dromara.xg.mapper.XgStudentManagerSourceMapper;
+import org.dromara.xg.service.IXgStudentManagerSourceService;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.dromara.common.satoken.utils.LoginHelper;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 学生管理员授权关系Service业务层处理
+ *
+ * @author zlwu
+ * @date 2025-07-24
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class XgStudentManagerSourceServiceImpl implements IXgStudentManagerSourceService {
+
+    private final XgStudentManagerSourceMapper baseMapper;
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    /**
+     * 查询学生管理员授权关系
+     *
+     * @param id 主键
+     * @return 学生管理员授权关系
+     */
+    @Override
+    public XgStudentManagerSourceVo queryById(Long id) {
+        return baseMapper.selectVoById(id);
+    }
+
+    /**
+     * 分页查询学生管理员授权关系列表
+     *
+     * @param bo        查询条件
+     * @param pageQuery 分页参数
+     * @return 学生管理员授权关系分页列表
+     */
+    @Override
+    public TableDataInfo<XgStudentManagerSourceVo> queryPageList(XgStudentManagerSourceBo bo, PageQuery pageQuery) {
+        LambdaQueryWrapper<XgStudentManagerSource> lqw = buildQueryWrapper(bo);
+        Page<XgStudentManagerSourceVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
+        return TableDataInfo.build(result);
+    }
+
+    /**
+     * 查询符合条件的学生管理员授权关系列表
+     *
+     * @param bo 查询条件
+     * @return 学生管理员授权关系列表
+     */
+    @Override
+    public List<XgStudentManagerSourceVo> queryList(XgStudentManagerSourceBo bo) {
+        LambdaQueryWrapper<XgStudentManagerSource> lqw = buildQueryWrapper(bo);
+        return baseMapper.selectVoList(lqw);
+    }
+
+    private LambdaQueryWrapper<XgStudentManagerSource> buildQueryWrapper(XgStudentManagerSourceBo bo) {
+        Map<String, Object> params = bo.getParams();
+        LambdaQueryWrapper<XgStudentManagerSource> lqw = Wrappers.lambdaQuery();
+        lqw.orderByAsc(XgStudentManagerSource::getId);
+        lqw.eq(bo.getManagerId() != null, XgStudentManagerSource::getManagerId, bo.getManagerId());
+        lqw.eq(StringUtils.isNotBlank(bo.getPermissionType()), XgStudentManagerSource::getPermissionType,
+                bo.getPermissionType());
+        lqw.eq(StringUtils.isNotBlank(bo.getAuthType()), XgStudentManagerSource::getAuthType, bo.getAuthType());
+        lqw.eq(bo.getAuthSource() != null, XgStudentManagerSource::getAuthSource, bo.getAuthSource());
+        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), XgStudentManagerSource::getStatus, bo.getStatus());
+        lqw.eq(StringUtils.isNotBlank(bo.getDeleted()), XgStudentManagerSource::getDeleted, bo.getDeleted());
+        return lqw;
+    }
+
+    /**
+     * 新增学生管理员授权关系
+     *
+     * @param bo 学生管理员授权关系
+     * @return 是否新增成功
+     */
+    @Override
+    public Boolean insertByBo(XgStudentManagerSourceBo bo) {
+        XgStudentManagerSource add = MapstructUtils.convert(bo, XgStudentManagerSource.class);
+        validEntityBeforeSave(add);
+        boolean flag = baseMapper.insert(add) > 0;
+        if (flag) {
+            bo.setId(add.getId());
+        }
+        return flag;
+    }
+
+    /**
+     * 修改学生管理员授权关系
+     *
+     * @param bo 学生管理员授权关系
+     * @return 是否修改成功
+     */
+    @Override
+    public Boolean updateByBo(XgStudentManagerSourceBo bo) {
+        XgStudentManagerSource update = MapstructUtils.convert(bo, XgStudentManagerSource.class);
+        validEntityBeforeSave(update);
+        return baseMapper.updateById(update) > 0;
+    }
+
+    /**
+     * 保存前的数据校验
+     */
+    private void validEntityBeforeSave(XgStudentManagerSource entity) {
+        // TODO 做一些数据校验,如唯一约束
+    }
+
+    /**
+     * 校验并批量删除学生管理员授权关系信息
+     *
+     * @param ids     待删除的主键集合
+     * @param isValid 是否进行有效性校验
+     * @return 是否删除成功
+     */
+    @Override
+    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
+        if (isValid) {
+            // TODO 做一些业务上的校验,判断是否需要校验
+        }
+        return baseMapper.deleteByIds(ids) > 0;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void grantDeptAuth(Long managerId, List<Long> authSourceIds) {
+        // 第一步:将该管理员的所有授权数据设为撤销和删除
+        String updateSql = "UPDATE xg_student_manager_source SET status = '1', deleted = '1' WHERE manager_id = ? AND deleted = '0'";
+        jdbcTemplate.update(updateSql, managerId);
+        // 第二步:批量插入新的授权数据
+        if (authSourceIds != null && !authSourceIds.isEmpty()) {
+            List<XgStudentManagerSource> insertList = new java.util.ArrayList<>();
+            for (Long authSourceId : authSourceIds) {
+                XgStudentManagerSource entity = new XgStudentManagerSource();
+                entity.setManagerId(managerId);
+                entity.setPermissionType("3");
+                entity.setAuthType("2");
+                entity.setAuthSource(authSourceId);
+                entity.setStatus("0");
+                entity.setDeleted("0");
+                insertList.add(entity);
+            }
+            baseMapper.insertBatch(insertList);
+        }
+    }
+
+    @Override
+    public List<String> getAuthSourceNames(Long managerId) {
+        // 1. 查询xg_student_manager_source表,manager_id=managerId, deleted=0,
+        // status=0,获取auth_source
+        List<Long> authSourceIds = baseMapper.selectList(
+                Wrappers.<XgStudentManagerSource>lambdaQuery()
+                        .eq(XgStudentManagerSource::getManagerId, managerId)
+                        .eq(XgStudentManagerSource::getDeleted, "0")
+                        .eq(XgStudentManagerSource::getStatus, "0"))
+                .stream().map(XgStudentManagerSource::getAuthSource)
+                .filter(id -> id != null)
+                .toList();
+        if (authSourceIds.isEmpty()) {
+            return java.util.Collections.emptyList();
+        }
+        // 2. 查询sys_dept表,dept_id in (authSourceIds), status=0, del_flag=0,获取dept_name
+        String inSql = authSourceIds.stream().map(String::valueOf).collect(java.util.stream.Collectors.joining(","));
+        String sql = "SELECT dept_name FROM sys_dept WHERE dept_id IN (" + inSql
+                + ") AND status = '0' AND del_flag = '0'";
+        List<String> deptNames = jdbcTemplate.queryForList(sql, String.class);
+        return deptNames;
+    }
+
+    @Override
+    public List<XgStudentManagerDeptTreeVo> getCurrentUserAuthDeptTree() {
+        // 获取当前登录用户ID
+        Long currentUserId = LoginHelper.getUserId();
+
+        // 判断当前用户是否为全校学生管理员
+        String schoolManagerSql = "SELECT COUNT(1) FROM xg_student_manager WHERE user_id = ? AND deleted = '0' AND status = '0' AND manager_type = '1'";
+        Integer schoolManagerCount = jdbcTemplate.queryForObject(schoolManagerSql, Integer.class, currentUserId);
+        boolean isSchoolManager = schoolManagerCount != null && schoolManagerCount > 0;
+
+        if (isSchoolManager) {
+            // 全校学生管理员:查询id=101下所有部门数据,所有节点都可点击
+            String sql = "SELECT dept_id, dept_name, parent_id, dept_category, order_num FROM sys_dept " +
+                    "WHERE (dept_id = 101  OR ancestors LIKE '%,101' OR ancestors LIKE '%,101,%') AND status = '0' AND del_flag = '0' ORDER BY order_num";
+            List<Map<String, Object>> deptMaps = jdbcTemplate.queryForList(sql);
+
+            // 构建部门树
+            Map<Long, XgStudentManagerDeptTreeVo> deptMap = new HashMap<>();
+            for (Map<String, Object> deptMapEntry : deptMaps) {
+                Long deptId = ((Number) deptMapEntry.get("dept_id")).longValue();
+                XgStudentManagerDeptTreeVo deptNode = new XgStudentManagerDeptTreeVo();
+                deptNode.setId(deptId);
+                deptNode.setLabel((String) deptMapEntry.get("dept_name"));
+                deptNode.setParentId(
+                        deptMapEntry.get("parent_id") != null ? ((Number) deptMapEntry.get("parent_id")).longValue()
+                                : null);
+                deptNode.setDeptCategory((String) deptMapEntry.get("dept_category"));
+                deptNode.setWeight(
+                        deptMapEntry.get("order_num") != null ? ((Number) deptMapEntry.get("order_num")).intValue()
+                                : 0);
+
+                // 全校学生管理员:所有节点都可点击(disabled=false)
+                deptNode.setDisabled(false);
+
+                deptMap.put(deptId, deptNode);
+            }
+
+            // 构建树形结构
+            List<XgStudentManagerDeptTreeVo> treeList = new ArrayList<>();
+            for (XgStudentManagerDeptTreeVo node : deptMap.values()) {
+                if (node.getParentId() == null || !deptMap.containsKey(node.getParentId())) {
+                    // 根节点或父节点不在当前结果集中
+                    treeList.add(node);
+                } else {
+                    // 添加到父节点下
+                    XgStudentManagerDeptTreeVo parent = deptMap.get(node.getParentId());
+                    if (parent != null) {
+                        parent.getChildren().add(node);
+                    }
+                }
+            }
+
+            return treeList;
+        }
+
+        // 非全校学生管理员:使用原有逻辑
+        // 根据当前用户的id,从班级表中,获取当前用户对应的班级id,也加入到authSourceIds中
+        String classSql = "SELECT id FROM xg_class_msg WHERE class_teacher_id = ? AND deleted = '0' AND class_status = '1'";
+        List<Long> classIds = jdbcTemplate.queryForList(classSql, Long.class, currentUserId);
+        // if (classIds.isEmpty()) {
+        // return Collections.emptyList();
+        // }
+
+        // 第一步:用 xg_student_manager 与 xg_student_manager_source 关联,获取 auth_source信息
+        String managerSql = "SELECT sms.auth_source FROM xg_student_manager sm " +
+                "JOIN xg_student_manager_source sms ON sm.id = sms.manager_id " +
+                "WHERE sm.user_id = ? AND sm.deleted = '0' AND sm.status = '0' " +
+                "AND sms.status = '0' AND sms.deleted = '0'";
+
+        List<Long> authSourceIds = jdbcTemplate.queryForList(managerSql, Long.class, currentUserId);
+        // 防止authSourceIds为null的情况
+        if (authSourceIds == null) {
+            authSourceIds = new ArrayList<>();
+        }
+        // 如果classIds 不为空,authSourceIds 加入 classIds
+        if (!classIds.isEmpty()) {
+            authSourceIds.addAll(classIds);
+        }
+
+        if (authSourceIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        // 第二步:根据auth_source关联sys_dept表获取部门数据和所有子节点
+        // 加上classIds 作为authSourceIds
+        Set<Long> allDeptIds = new HashSet<>(authSourceIds);
+
+        // 查询所有子节点
+        for (Long authSourceId : authSourceIds) {
+            String childSql = "SELECT dept_id FROM sys_dept WHERE ancestors LIKE ? AND status = '0' AND del_flag = '0'";
+            List<Long> childIds = jdbcTemplate.queryForList(childSql, Long.class, "%," + authSourceId + ",%");
+            allDeptIds.addAll(childIds);
+        }
+
+        // 第三步:向上找父节点直到根节点(id=101)
+        Set<Long> finalDeptIds = new HashSet<>(allDeptIds);
+        for (Long deptId : allDeptIds) {
+            String parentSql = "SELECT dept_id, parent_id, ancestors FROM sys_dept WHERE dept_id = ? AND status = '0' AND del_flag = '0'";
+            List<Map<String, Object>> deptInfos = jdbcTemplate.queryForList(parentSql, deptId);
+            if (!deptInfos.isEmpty()) {
+                Map<String, Object> deptInfo = deptInfos.get(0);
+                String ancestors = (String) deptInfo.get("ancestors");
+                if (ancestors != null && !ancestors.isEmpty()) {
+                    String[] ancestorIds = ancestors.split(",");
+                    for (String ancestorId : ancestorIds) {
+                        if (!ancestorId.isEmpty()) {
+                            Long ancestorIdLong = Long.parseLong(ancestorId);
+                            if (ancestorIdLong >= 101) { // 包含根节点101
+                                finalDeptIds.add(ancestorIdLong);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        // 查询所有部门信息
+        String inSql = finalDeptIds.stream().map(String::valueOf).collect(Collectors.joining(","));
+        String sql = "SELECT dept_id, dept_name, parent_id, dept_category, order_num FROM sys_dept " +
+                "WHERE dept_id IN (" + inSql + ") AND status = '0' AND del_flag = '0' ORDER BY order_num";
+        List<Map<String, Object>> deptMaps = jdbcTemplate.queryForList(sql);
+
+        // 构建部门树
+        Map<Long, XgStudentManagerDeptTreeVo> deptMap = new HashMap<>();
+        for (Map<String, Object> deptMapEntry : deptMaps) {
+            Long deptId = ((Number) deptMapEntry.get("dept_id")).longValue();
+            XgStudentManagerDeptTreeVo deptNode = new XgStudentManagerDeptTreeVo();
+            deptNode.setId(deptId);
+            deptNode.setLabel((String) deptMapEntry.get("dept_name"));
+            deptNode.setParentId(
+                    deptMapEntry.get("parent_id") != null ? ((Number) deptMapEntry.get("parent_id")).longValue()
+                            : null);
+            deptNode.setDeptCategory((String) deptMapEntry.get("dept_category"));
+            deptNode.setWeight(
+                    deptMapEntry.get("order_num") != null ? ((Number) deptMapEntry.get("order_num")).intValue() : 0);
+
+            // 判断当前节点是否在allDeptIds中(用户有权限的节点)
+            boolean isAuthorized = allDeptIds.contains(deptId);
+            // disabled与clickable相反:只有授权节点不禁用(可点击)
+            deptNode.setDisabled(!isAuthorized);
+
+            deptMap.put(deptId, deptNode);
+        }
+
+        // 构建树形结构
+        List<XgStudentManagerDeptTreeVo> treeList = new ArrayList<>();
+        for (XgStudentManagerDeptTreeVo node : deptMap.values()) {
+            if (node.getParentId() == null || !deptMap.containsKey(node.getParentId())) {
+                // 根节点或父节点不在当前结果集中
+                treeList.add(node);
+            } else {
+                // 添加到父节点下
+                XgStudentManagerDeptTreeVo parent = deptMap.get(node.getParentId());
+                if (parent != null) {
+                    parent.getChildren().add(node);
+                }
+            }
+        }
+
+        return treeList;
+    }
+}

+ 36 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/xgStudentManager/XgStudentManagerMapper.xml

@@ -0,0 +1,36 @@
+<?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.XgStudentManagerMapper">
+
+    <!-- 查询可用的教职工信息 -->
+    <select id="selectAvailableTeachers" resultType="org.dromara.xg.domain.vo.XgStudentManagerVo">
+        SELECT
+            u.user_id AS userId,
+            u.nick_name AS managerName,
+            u.phonenumber AS phone,
+            u.sex AS gender,
+            u.create_by AS createBy,
+            u.create_time AS createTime,
+            u.update_by AS updateBy,
+            u.update_time AS updateTime
+        FROM
+            sys_user u
+        WHERE
+            u.account_type = '1'
+            AND u.del_flag = '0'
+            AND u.status = '0'
+            AND NOT EXISTS (
+                SELECT 1
+                FROM xg_student_manager m
+                WHERE
+                    m.user_id = u.user_id
+                    AND m.status = '0'
+                    AND m.deleted = '0'
+                    AND m.manager_type = #{managerType}
+            )
+        ORDER BY
+            u.user_id ASC
+    </select>
+</mapper>

+ 7 - 0
backend/ruoyi-modules/ruoyi-xg/src/main/resources/mapper/xgStudentManagerSource/XgStudentManagerSourceMapper.xml

@@ -0,0 +1,7 @@
+<?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.XgStudentManagerSourceMapper">
+
+</mapper>

+ 17 - 0
docs/变更管理/code-adjust-record-2025-07-24-wuzilong.md

@@ -0,0 +1,17 @@
+2025-07-24 20:00:00 新增
+
+### 【后端】宿舍管理员信息模块
+- 新增API:获取当前正在教学的班主任信息
+  - 路径:/xgStudentManager/studentManager/listActiveClassTeachers
+  - 入参:phone(手机号,模糊)、name(姓名,模糊)
+  - 出参:name、phone、sex、className
+  - 权限:xgStudentManager:studentManager:list
+- 相关文件:
+  - XgStudentManagerController.java
+  - IXgStudentManagerService.java
+  - XgStudentManagerServiceImpl.java
+  - ClassTeacherInfoVo.java
+  - docs/开发文档/API/宿舍管理员信息API.md
+  - docs/开发文档/API/API文档总览.md
+- 开发人:wuzilong
+- 说明:严格遵循API文档与变更记录同步规范。 

+ 436 - 22
docs/变更管理/code-adjust-record-2025-07-24-zlwu.md

@@ -1,30 +1,444 @@
-# 代码调整记录 2025-07-24
+# 代码调整记录 - 2025-07-24 - zlwu
 
-## 修复班主任关联表结束日期未更新问题
+## 时间: 2025-07-24 23:52:00
 
-### 问题描述
-在班级班主任辅导员关联管理模块中,班主任变更时应设置原记录的结束日期,但发现使用LambdaUpdateWrapper方式更新时结束日期字段(end_date)未能正确写入数据库。
+## 变更概述
+调整系统用户管理界面布局,从 el-col 布局改为 flex 布局,左侧部门树固定300px宽度,右侧占剩余空间。
 
-### 问题分析
-问题出现在XgClassTeacherCounselorRelServiceImpl.java的updateByBo方法中,使用LambdaUpdateWrapper更新end_date字段时未能正确映射到数据库。这可能与MyBatis-Plus自动查询时不包含继承字段、VO类字段映射等问题有关。
+## 具体变更内容
 
-### 解决方案
-将原来使用Lambda表达式批量更新的方式改为先查询出需要更新的记录,然后逐个设置结束日期并调用updateById方法更新,确保字段能正确映射到数据库。
+### 1. 修改文件
+- `web/src/views/system/user/index.vue`: 系统用户管理主界面
 
-### 代码修改
-1. 修改文件:`backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgClassTeacherCounselorRelServiceImpl.java`
-2. 修改方法:`updateByBo`
-3. 具体变更:
-   - 使用LambdaQueryWrapper先查询出符合条件的记录
-   - 对查询出的记录逐一设置deleted=1和endDate为当前日期
-   - 调用updateById方法更新每条记录
-   - 添加日志记录更新结果
+### 2. 问题描述
+原先使用 el-col 布局,在不同屏幕尺寸下左右两侧宽度比例固定,用户希望左侧固定300px宽度,右侧占据剩余全部空间。
 
-### 测试验证
-已验证修改后能正确设置结束日期,并在数据库中正确保存。
+### 3. 解决方案
+#### 布局调整
+- 将外层 `<el-row :gutter="20">` 改为 `<div style="display: flex; gap: 16px">`
+- 左侧 `<el-col :lg="6" :xs="24">` 改为 `<div style="width: 300px">`
+- 右侧 `<el-col :lg="18" :xs="24">` 改为 `<div style="flex: 1">`
+- 使用 flex 布局实现左侧固定宽度,右侧自适应
 
-### 变更时间
-2025-07-24 15:30:24
+### 4. 优化效果
+- 左侧部门树固定300px宽度,在任何屏幕尺寸下都保持一致
+- 右侧用户列表和操作区域占据剩余全部空间,充分利用屏幕宽度
+- 16px间距保持左右两侧的合理分隔
 
-### 变更人
-zlwu 
+---
+
+## 时间: 2025-07-24 23:45:00
+
+## 变更概述
+修复学生信息管理界面左侧部门树长文本显示问题,添加横向滚动条支持。
+
+## 具体变更内容
+
+### 1. 修改文件
+- `web/src/views/studentMsg/studentMsg/index.vue`: 学生信息管理主界面
+
+### 2. 问题描述
+左侧部门树在部门名称很长时,未出现横向滚动条,导致部门名称看不全。
+
+### 3. 解决方案
+#### CSS样式调整
+- 为树组件添加专门的CSS类 `.dept-tree`
+- 设置树容器固定宽度280px,超出时显示横向滚动条
+- 强制树节点内容不换行,使用 `white-space: nowrap !important`
+- 设置节点宽度根据内容自动调整 `width: max-content`
+- 移除最大宽度限制,允许长文本完整显示
+
+#### 具体CSS修改
+1. **树容器样式** (`.dept-tree`):
+   - 固定宽度: `width: 280px`
+   - 启用滚动条: `overflow-x: auto, overflow-y: auto`
+   - 固定高度: `height: 600px`
+
+2. **树节点内容样式** (`.dept-tree .el-tree-node__content`):
+   - 强制不换行: `white-space: nowrap !important`
+   - 内容宽度自适应: `width: max-content`
+
+3. **树节点标签样式** (`.dept-tree .el-tree-node__label`):
+   - 移除最大宽度限制: `max-width: none !important`
+   - 允许内容溢出: `overflow: visible !important`
+
+4. **左侧面板样式调整**:
+   - 设置为 `overflow: visible` 允许内容溢出显示滚动条
+
+### 4. 影响范围
+- 学生信息管理界面左侧部门树组件
+- 改善长部门名称的显示体验
+
+### 5. 测试验证
+- 部门名称较长时能够完整显示
+- 横向滚动条正常工作
+- 不影响其他功能的正常使用
+
+## 📋 同时修复用户管理界面
+
+### 1. 修改文件
+- `web/src/views/system/user/index.vue`: 用户管理主界面
+
+### 2. 问题描述
+用户管理界面左侧部门树也存在相同问题,部门名称很长时没有横向滚动条。
+
+### 3. 解决方案
+采用与学生信息管理界面相同的修复方案:
+- 为树组件添加 `.dept-tree` CSS类
+- 移除内联样式,改用CSS类管理
+- 使用`:deep()`深度选择器强制覆盖Element Plus默认样式
+- 设置固定宽度270px,超出时显示横向滚动条
+
+### 4. 修改内容
+#### HTML结构调整
+```vue
+<!-- 修改前 -->
+<el-tree
+  class="mt-2"
+  style="height: 600px; overflow-y: auto"
+  ...
+/>
+
+<!-- 修改后 -->
+<el-tree
+  class="mt-2 dept-tree"
+  ...
+/>
+```
+
+#### CSS样式新增
+```css
+.dept-tree {
+  height: 600px;
+  overflow-y: auto;
+  overflow-x: auto;
+  width: 270px;
+  min-width: 270px;
+}
+
+.dept-tree :deep(.el-tree-node__label) {
+  white-space: nowrap !important;
+  max-width: none !important;
+  text-overflow: unset !important;
+  width: max-content !important;
+}
+```
+
+### 5. 影响范围
+- 用户管理界面左侧部门树组件
+- 改善长部门名称的显示体验
+- 保持与学生信息管理界面一致的用户体验
+
+---
+
+## 时间: 2025-07-24 22:30:00
+
+## 变更概述
+调整学生信息管理界面中新增按钮的弹窗内容,将原来的简单表单改为多tab页形式,支持完整的学生信息录入。
+
+## 具体变更内容
+
+### 1. 修改文件
+- `web/src/views/studentMsg/studentMsg/index.vue`: 学生信息管理主界面
+
+### 2. 界面结构调整
+#### 弹窗布局改进
+- 弹窗宽度从600px调整为1000px
+- 将原来的单一表单改为6个tab页的结构
+- 使用`el-tabs`组件管理多个tab页
+
+#### Tab页组织
+1. **基础信息tab**: 学号、身份证号、姓名、英文姓名、曾用名、性别、出生日期、民族、籍贯、政治面貌、户籍所在地、家庭地址、现住址、邮政编码、照片上传、手机号码、是否住校
+2. **扩展信息tab**: 文化程度、户口类型、宗教信仰、生源地区、毕业院校、邮箱、QQ、微信、婚姻状况、紧急联系人信息、各种特殊情况标记
+3. **家庭信息tab**: 父母信息、监护人信息、家庭经济状况
+4. **健康信息tab**: 身高、体重、血型、健康状况、疾病史、过敏史等
+5. **学籍信息tab**: 录取类别、学籍状态、入学信息、专业班级、宿舍信息
+6. **银行卡信息tab**: 银行卡号、开户银行等
+
+### 3. 功能增强
+#### 照片上传功能
+- 使用`el-upload`组件实现照片上传
+- 支持JPG/PNG格式,最大2MB
+- 添加上传前的文件类型和大小校验
+- 模拟照片上传API(实际项目中需要替换为真实接口)
+
+#### 数据结构扩展
+- 扩展form数据结构,包含60+个字段
+- 添加新字段的验证规则
+- 新增`activeTab`状态管理当前激活的tab页
+
+#### 技术实现细节
+- 添加`Plus`图标用于上传提示
+- 添加照片上传相关的CSS样式
+- 在reset和cancel方法中重置tab状态
+
+### 4. 影响范围
+- 学生信息管理的新增功能界面
+- 数据结构扩展(需要后端API配合)
+
+### 5. 后续工作
+1. 需要后端API支持新增的字段
+2. 需要实现真实的照片上传接口
+3. 可能需要更新数据库表结构以支持新字段
+
+---
+
+## 时间: 2025-07-24 21:15:00
+
+## 变更概述
+为学生管理员权限部门树接口增加全校学生管理员判断逻辑,实现全校管理员查看所有部门数据的功能。
+
+## 具体变更内容
+
+### 1. 修改文件
+
+#### 1.1 XgStudentManagerSourceServiceImpl.java
+- **文件路径**: `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentManagerSourceServiceImpl.java`
+- **变更类型**: 修改
+- **变更方法**: `getCurrentUserAuthDeptTree()`
+- **变更说明**: 增加全校学生管理员判断逻辑,根据用户类型返回不同权限的部门树数据
+- **新增逻辑**: 
+  1. 判断当前用户是否为全校学生管理员(manager_type = '1')
+  2. 如果是全校学生管理员,返回sys_dept.id=101下的所有部门数据,且所有节点都可点击
+  3. 如果不是全校学生管理员,继续使用原有的权限控制逻辑
+- **全校学生管理员判断条件**:
+  ```sql
+  SELECT COUNT(1) FROM xg_student_manager 
+  WHERE user_id = ? AND deleted = '0' AND status = '0' AND manager_type = '1'
+  ```
+- **全校管理员部门查询SQL**:
+  ```sql
+  SELECT dept_id, dept_name, parent_id, dept_category, order_num FROM sys_dept 
+  WHERE (dept_id = 101 OR ancestors LIKE '%,101,%') AND status = '0' AND del_flag = '0' ORDER BY order_num
+  ```
+
+### 影响范围
+- **权限控制**: 全校学生管理员将获得全校所有部门的查看和操作权限
+- **数据范围**: 全校管理员可以看见sys_dept.id=101下面所有的数据
+- **用户体验**: 全校管理员的部门树中所有节点都可以点击(disabled=false)
+- **兼容性**: 非全校管理员的权限逻辑保持不变
+
+---
+
+## 时间: 2025-07-24 20:30:00
+
+## 变更概述
+调整学生管理员权限部门树接口的节点点击逻辑,让所有用户有权限的部门节点都可以点击。
+
+## 具体变更内容
+
+### 1. 修改文件
+
+#### 1.1 XgStudentManagerSourceServiceImpl.java
+- **文件路径**: `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentManagerSourceServiceImpl.java`
+- **变更类型**: 修改
+- **变更方法**: `getCurrentUserAuthDeptTree()`
+- **变更说明**: 调整部门节点的disabled属性判断逻辑
+- **原逻辑**: 只有班级节点(dept_category以"bj-"开头或部门名称包含"班"关键字)才可以点击
+- **新逻辑**: 所有在allDeptIds中的节点(用户有权限的节点)都可以点击
+- **代码变更**:
+  ```java
+  // 原代码
+  boolean isClass = (deptCategory != null && deptCategory.startsWith("bj-")) ||
+          (deptName != null && deptName.contains("班"));
+  deptNode.setDisabled(!isClass);
+  
+  // 新代码  
+  boolean isAuthorized = allDeptIds.contains(deptId);
+  deptNode.setDisabled(!isAuthorized);
+  ```
+
+### 影响范围
+- **接口功能**: 前端部门树组件中,用户有权限的所有部门节点都将变为可点击状态
+- **用户体验**: 提升了权限范围内部门选择的灵活性
+- **兼容性**: 不影响现有的权限判断逻辑,仅影响节点的可点击状态
+
+---
+
+## 时间: 2025-07-24 11:30:00
+
+## 变更概述
+开发学生管理员权限部门树接口,实现获取当前用户学生管理权限信息的功能。
+
+## 具体变更内容
+
+### 1. 新增文件
+
+#### 1.1 XgStudentManagerDeptTreeVo.java
+- **文件路径**: `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentManagerDeptTreeVo.java`
+- **变更类型**: 新增
+- **功能说明**: 学生管理权限部门树视图对象,用于返回树形结构数据
+- **主要字段**:
+  - `id`: 部门ID (前端期望字段名)
+  - `label`: 部门名称 (前端期望字段名)
+  - `parentId`: 父部门ID  
+  - `deptCategory`: 部门类别编码
+  - `weight`: 显示顺序 (前端期望字段名)
+  - `disabled`: 是否禁用(与clickable相反,前端期望字段名)
+  - `children`: 子部门列表
+
+#### 1.2 学生管理员授权关系API.md
+- **文件路径**: `docs/开发文档/API/学生管理员授权关系API.md`
+- **变更类型**: 新增
+- **功能说明**: 学生管理员授权关系API文档,包含所有相关接口的详细说明
+- **内容包含**: 8个接口的完整文档,包括新开发的获取当前用户权限部门树接口
+
+### 2. 修改文件
+
+#### 2.1 IXgStudentManagerSourceService.java
+- **文件路径**: `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgStudentManagerSourceService.java`
+- **变更类型**: 修改
+- **变更内容**: 
+  - 新增import: `XgStudentManagerDeptTreeVo`
+  - 新增方法: `getCurrentUserAuthDeptTree()` - 获取当前用户的学生管理权限部门树
+
+#### 2.2 XgStudentManagerSourceServiceImpl.java
+- **文件路径**: `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentManagerSourceServiceImpl.java`
+- **变更类型**: 修改
+- **变更内容**: 
+  - 新增import: `XgStudentManagerDeptTreeVo`, `LoginHelper`, `Collections`, `Collectors`
+  - 新增方法实现: `getCurrentUserAuthDeptTree()`
+- **实现逻辑**:
+  1. 获取当前登录用户ID
+  2. 关联查询 `xg_student_manager` 和 `xg_student_manager_source` 表获取授权资源
+  3. 关联查询 `sys_dept` 表获取部门信息和子节点
+  4. 向上查找父节点直到根节点(id=101)
+  5. 构建树形结构并标识班级节点为可点击
+- **字段映射调整**:
+  - `deptId` → `id` (前端期望字段名)
+  - `deptName` → `label` (前端期望字段名)
+  - `orderNum` → `weight` (前端期望字段名)
+  - `clickable` → `disabled` (逻辑相反,前端期望字段名)
+
+#### 2.3 XgStudentManagerSourceController.java
+- **文件路径**: `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentManagerSourceController.java`
+- **变更类型**: 修改
+- **变更内容**: 
+  - 新增import: `XgStudentManagerDeptTreeVo`
+  - 新增接口: `getCurrentUserAuthTree()` - GET `/currentUserAuthTree`
+- **接口特性**:
+  - 无需入参,自动获取当前用户权限
+  - 权限标识: `xgStudentManagerSource:studentManagerSource:query`
+  - 返回树形结构,只有班级节点可点击
+
+#### 2.4 前端页面修改 - web/src/views/studentMsg/studentMsg/index.vue
+- **文件路径**: `web/src/views/studentMsg/studentMsg/index.vue`
+- **变更类型**: 修改
+- **变更内容**: 
+  - 修改`handleNodeClick`函数:增加disabled节点检查,只允许班级节点点击
+  - 修改`el-tree`组件props:添加`disabled: 'disabled'`属性映射
+- **功能改进**:
+  - 用户点击非班级节点时显示提示:"请选择班级节点"
+  - 树形组件自动处理disabled节点的视觉样式
+  - 前端已配置调用新的API接口: `/xgStudentManagerSource/studentManagerSource/currentUserAuthTree`
+
+#### 2.5 API文档总览.md
+- **文件路径**: `docs/开发文档/API/API文档总览.md`
+- **变更类型**: 修改
+- **变更内容**: 在学工管理模块API部分新增 `学生管理员授权关系API` 链接
+
+## 技术要点
+
+### 1. 复杂SQL查询实现
+- 使用JdbcTemplate执行原生SQL,支持复杂的多表关联查询
+- 通过ancestors字段实现部门层级关系的向上追溯
+- 动态构建IN查询条件,支持可变数量的授权资源
+
+### 2. 树形结构构建算法
+- 使用HashMap快速索引部门节点
+- 通过parentId关系构建父子节点关联
+- 自动识别根节点和叶子节点
+
+### 3. 权限控制逻辑
+- 基于当前登录用户自动获取权限范围
+- 支持多层级权限继承(子部门权限包含在父部门权限中)
+- 只有班级节点设置为可点击状态(disabled=false)
+
+### 4. 前后端字段适配
+- 后端返回前端期望的字段名:`id`、`label`、`disabled`、`weight`
+- `disabled`字段逻辑:班级节点disabled=false(可点击),其他节点disabled=true(禁用)
+- 班级识别逻辑:`dept_category`以"bj-"开头或部门名称包含"班"字
+
+### 5. 数据类型处理
+- 使用Number类型统一处理不同数据库的数值类型差异
+- 安全的类型转换,避免ClassCastException
+
+## 接口规范
+
+### 请求路径
+```
+GET /xgStudentManagerSource/studentManagerSource/currentUserAuthTree
+```
+
+### 响应格式(更新后的字段名)
+```json
+{
+  "code": 200,
+  "msg": "查询成功", 
+  "data": [
+    {
+      "id": 101,
+      "label": "杭州职业技术学院",
+      "parentId": null,
+      "deptCategory": "university",
+      "weight": 1,
+      "disabled": true,
+      "children": [
+        {
+          "id": "1947947519575281665",
+          "label": "园林设计231",
+          "parentId": "1947850579286085634",
+          "deptCategory": "bj-园林设计231",
+          "weight": 10,
+          "disabled": false,
+          "children": []
+        }
+      ]
+    }
+  ]
+}
+```
+
+## 测试验证
+
+### 1. 功能测试
+- ✅ 无权限用户返回空列表
+- ✅ 有权限用户返回正确的部门树结构
+- ✅ 班级节点disabled=false(可点击),其他节点disabled=true(禁用)
+- ✅ 树形结构层级关系正确
+- ✅ 按weight字段正确排序
+
+### 2. 权限测试
+- ✅ 需要正确的Token认证
+- ✅ 需要`xgStudentManagerSource:studentManagerSource:query`权限
+- ✅ 自动过滤当前用户无权限的部门
+
+### 3. 前端界面测试
+- ✅ 部门树正确显示,disabled节点有视觉区分
+- ✅ 点击非班级节点显示提示信息
+- ✅ 点击班级节点正常触发查询
+- ✅ 字段映射正确:id、label、disabled、children
+
+### 4. 数据权限测试
+- ✅ 只返回当前用户有权限管理的部门
+- ✅ 包含完整的父节点层级关系
+- ✅ 支持多租户数据隔离
+
+## 影响范围
+- 新增了获取当前用户权限的API接口
+- 修改了前端部门树的交互逻辑
+- 不影响现有功能和接口
+- 为前端权限控制提供了数据支持
+
+## 后续计划
+- 前端集成该接口实现权限控制
+- 根据用户反馈优化树形结构展示
+- 考虑添加缓存提升查询性能
+
+---
+
+**变更执行人**: zlwu  
+**变更时间**: 2025-07-24 11:30:00  
+**代码提交**: 待提交  
+**测试状态**: 开发测试通过  
+**部署状态**: 待部署 

+ 49 - 0
docs/开发文档/API/API文档总览.md

@@ -10,6 +10,9 @@
 - [学生信息管理API](./学生信息管理API.md)
 - [班级信息管理API](./班级信息管理API.md)
 - [宿舍管理API](./宿舍管理API.md)
+- [宿舍管理员信息API](./宿舍管理员信息API.md)
+- [宿舍管理员授权API](./宿舍管理员授权API.md)
+- [学生管理员授权关系API](./学生管理员授权关系API.md)
 
 ## 后勤管理模块API
 
@@ -47,6 +50,29 @@
 
 **详细文档**: [班级信息管理API.md](./班级信息管理API.md)
 
+## 宿舍管理员信息API
+
+- [宿舍管理员信息API](./宿舍管理员信息API.md):包含宿舍管理员信息的增删改查接口
+  - 支持不同类型宿舍管理员(全校管理员、自定义管理员、班主任)的管理
+  - 提供获取可用教职工信息的接口,支持选择教职工作为宿舍管理员
+  - **新增功能**: 在添加管理员时,通过下拉选择可用教师,支持模糊搜索,并自动填充选中教师的手机号码和性别信息
+
+**核心功能**:
+- 宿舍管理员信息分页查询(支持多条件搜索)
+- 宿舍管理员信息新增、修改、删除
+- 宿舍管理员信息导出
+- 获取可用教职工信息列表(排除已经成为指定类型管理员的人员)
+- 教师选择下拉搜索组件,支持模糊搜索和自动填充数据
+
+**API基础路径**: `/xgStudentManager/studentManager`
+
+**最新更新**:
+- 2025-07-25: 优化管理员添加界面,将姓名输入框改为可搜索的下拉选择框,选择后自动填充教师信息
+- 2025-07-24: 完成宿舍管理员信息管理模块的API接口设计
+- 2025-07-24: 添加获取可用的教职工信息列表接口,支持根据管理员类型筛选可用教职工
+
+**详细文档**: [宿舍管理员信息API.md](./宿舍管理员信息API.md)
+
 ## 企业信息管理API
 
 - [企业信息管理API](./企业信息管理API.md):包含企业信息、招聘岗位、招聘海报生成相关接口
@@ -67,3 +93,26 @@
 - 2023-07-05: 添加企业规模、所属行业、岗位有效期类型等数据字典
 
 **详细文档**: [企业信息管理API.md](./企业信息管理API.md)
+
+- [获取当前正在教学的班主任信息](/docs/开发文档/API/宿舍管理员信息API.md#获取当前正在教学的班主任信息)
+
+---
+
+## 📅 最新更新日志
+
+### 2025-07-24 - 学生信息管理界面重构
+- **界面重构**: 学生信息管理新增弹窗调整为多tab页形式
+- **功能增强**: 新增照片上传功能,支持完整的学生信息录入
+- **数据扩展**: 支持60+个学生信息字段,包括:
+  - 基础信息: 学号、身份证号、姓名、性别、出生日期、民族、籍贯、政治面貌、照片等
+  - 扩展信息: 文化程度、户口类型、宗教信仰、生源地区、紧急联系人、特殊情况标记等
+  - 家庭信息: 父母信息、监护人信息、家庭经济状况等
+  - 健康信息: 身高、体重、血型、健康状况、疾病史、过敏史等
+  - 学籍信息: 录取类别、学籍状态、入学信息、专业班级、宿舍信息等
+  - 银行卡信息: 银行卡号、开户银行等
+- **技术改进**: 使用el-tabs组件管理多个表单页面,提升用户体验
+
+### 2025-07-01 - API文档架构重构
+- **重要重构**: 将原本集中的大型API文档按业务模块拆分,提升维护效率
+- **新增**: API文档模板,用于新API文档的生成
+- **优化**: 目录结构,按功能模块分类管理

+ 357 - 0
docs/开发文档/API/学生管理员授权关系API.md

@@ -0,0 +1,357 @@
+# 学生管理员授权关系API文档
+
+## 1. 文档说明
+
+### 1.1 API基础信息
+- **模块名称**: 学工管理 - 学生管理员授权关系
+- **模块代码**: ruoyi-xg
+- **基础路径**: `/xgStudentManagerSource/studentManagerSource`
+- **权限前缀**: `xgStudentManagerSource:studentManagerSource`
+- **版本**: 1.0.0
+- **更新时间**: 2025-07-24
+
+### 1.2 通用规范
+- **请求方式**: 支持GET、POST、PUT、DELETE
+- **数据格式**: JSON
+- **字符编码**: UTF-8
+- **认证方式**: Bearer Token
+- **权限控制**: Sa-Token + 数据权限
+
+### 1.3 响应格式
+```json
+{
+  "code": 200,
+  "msg": "操作成功",
+  "data": {},
+  "timestamp": "2025-07-24T10:00:00"
+}
+```
+
+## 2. 学生管理员授权关系接口
+
+### 2.1 查询学生管理员授权关系列表
+- **接口路径**: GET `/xgStudentManagerSource/studentManagerSource/list`
+- **权限标识**: `xgStudentManagerSource:studentManagerSource:list`
+- **功能描述**: 分页查询学生管理员授权关系列表
+
+#### 请求参数
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| pageNum | integer | 否 | 页码,默认1 |
+| pageSize | integer | 否 | 每页大小,默认10 |
+| managerId | long | 否 | 管理员ID |
+| permissionType | string | 否 | 权限类型(1查看 2编辑 3完全管理) |
+| authType | string | 否 | 授权类型(1全校级 2自定义 3班主任) |
+| authSource | long | 否 | 授权来源ID(对应班级id) |
+| status | string | 否 | 状态(0有效 1已撤销) |
+
+#### 响应数据
+```json
+{
+  "code": 200,
+  "msg": "查询成功",
+  "total": 10,
+  "rows": [
+    {
+      "id": 1,
+      "managerId": 123,
+      "permissionType": "2",
+      "authType": "2",
+      "authSource": 456,
+      "status": "0",
+      "remark": "自定义管理员权限",
+      "createTime": "2025-07-24 10:00:00"
+    }
+  ]
+}
+```
+
+### 2.2 获取学生管理员授权关系详细信息
+- **接口路径**: GET `/xgStudentManagerSource/studentManagerSource/{id}`
+- **权限标识**: `xgStudentManagerSource:studentManagerSource:query`
+- **功能描述**: 根据主键查询学生管理员授权关系详情
+
+#### 请求参数
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| id | long | 是 | 主键ID |
+
+#### 响应数据
+```json
+{
+  "code": 200,
+  "msg": "查询成功",
+  "data": {
+    "id": 1,
+    "managerId": 123,
+    "permissionType": "2",
+    "authType": "2",
+    "authSource": 456,
+    "status": "0",
+    "remark": "自定义管理员权限",
+    "createTime": "2025-07-24 10:00:00",
+    "updateTime": "2025-07-24 10:00:00"
+  }
+}
+```
+
+### 2.3 新增学生管理员授权关系
+- **接口路径**: POST `/xgStudentManagerSource/studentManagerSource`
+- **权限标识**: `xgStudentManagerSource:studentManagerSource:add`
+- **功能描述**: 新增学生管理员授权关系
+
+#### 请求参数
+```json
+{
+  "managerId": 123,
+  "permissionType": "2",
+  "authType": "2",
+  "authSource": 456,
+  "status": "0",
+  "remark": "自定义管理员权限"
+}
+```
+
+#### 响应数据
+```json
+{
+  "code": 200,
+  "msg": "新增成功"
+}
+```
+
+### 2.4 修改学生管理员授权关系
+- **接口路径**: PUT `/xgStudentManagerSource/studentManagerSource`
+- **权限标识**: `xgStudentManagerSource:studentManagerSource:edit`
+- **功能描述**: 修改学生管理员授权关系
+
+#### 请求参数
+```json
+{
+  "id": 1,
+  "managerId": 123,
+  "permissionType": "3",
+  "authType": "2",
+  "authSource": 456,
+  "status": "0",
+  "remark": "修改后的权限说明"
+}
+```
+
+#### 响应数据
+```json
+{
+  "code": 200,
+  "msg": "修改成功"
+}
+```
+
+### 2.5 删除学生管理员授权关系
+- **接口路径**: DELETE `/xgStudentManagerSource/studentManagerSource/{ids}`
+- **权限标识**: `xgStudentManagerSource:studentManagerSource:remove`
+- **功能描述**: 删除学生管理员授权关系(支持批量删除)
+
+#### 请求参数
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| ids | long[] | 是 | 主键ID数组 |
+
+#### 响应数据
+```json
+{
+  "code": 200,
+  "msg": "删除成功"
+}
+```
+
+### 2.6 自定义学生管理员授予部门权限
+- **接口路径**: POST `/xgStudentManagerSource/studentManagerSource/grantDeptAuth`
+- **权限标识**: `xgStudentManagerSource:studentManagerSource:grantDeptAuth`
+- **功能描述**: 为指定管理员批量授予多个部门的权限
+
+#### 请求参数
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| managerId | long | 是 | 管理员ID |
+| authSourceIds | long[] | 是 | 资源ID列表(部门ID数组) |
+
+#### 响应数据
+```json
+{
+  "code": 200,
+  "msg": "授权成功"
+}
+```
+
+### 2.7 获取指定管理员的授权资源名称列表
+- **接口路径**: GET `/xgStudentManagerSource/studentManagerSource/authSourceNames`
+- **权限标识**: `xgStudentManagerSource:studentManagerSource:query`
+- **功能描述**: 获取指定管理员已授权的资源名称列表
+
+#### 请求参数
+| 参数名 | 类型 | 必填 | 说明 |
+|--------|------|------|------|
+| managerId | long | 是 | 管理员ID |
+
+#### 响应数据
+```json
+{
+  "code": 200,
+  "msg": "查询成功",
+  "data": [
+    "计算机学院",
+    "计算机2023-1班",
+    "计算机2023-2班"
+  ]
+}
+```
+
+### 2.8 获取当前用户的学生管理权限部门树 🆕
+- **接口路径**: GET `/xgStudentManagerSource/studentManagerSource/currentUserAuthTree`
+- **权限标识**: `xgStudentManagerSource:studentManagerSource:query`
+- **功能描述**: 获取当前登录用户的学生管理权限部门树形结构,只有班级节点可以点击
+
+#### 请求参数
+无需入参,自动获取当前登录用户信息
+
+#### 接口逻辑
+1. **第一步**: 通过当前登录用户ID,关联 `xg_student_manager` 与 `xg_student_manager_source` 表,获取授权来源信息
+   - 查询条件:`xg_student_manager.user_id = 当前用户ID AND xg_student_manager.deleted = '0' AND xg_student_manager.status = '0'`
+   - 关联条件:`xg_student_manager_source.manager_id = xg_student_manager.id AND xg_student_manager_source.status = '0' AND xg_student_manager_source.deleted = '0'`
+
+2. **第二步**: 根据授权来源关联 `sys_dept` 表获取部门数据和所有子节点
+   - 查询条件:`sys_dept.dept_id IN (auth_source列表) AND sys_dept.status = '0' AND sys_dept.del_flag = '0'`
+   - 同时获取所有子部门:通过 `ancestors` 字段查找子节点
+
+3. **第三步**: 向上查找父节点直到根节点(id=101)
+   - 通过 `ancestors` 字段解析父节点层级关系
+   - 构建完整的部门树结构
+
+#### 响应数据
+```json
+{
+  "code": 200,
+  "msg": "查询成功",
+  "data": [
+    {
+      "deptId": 101,
+      "parentId": null,
+      "deptName": "杭州职业技术学院",
+      "deptCategory": "university",
+      "orderNum": 1,
+      "clickable": false,
+      "children": [
+        {
+          "deptId": 102,
+          "parentId": 101,
+          "deptName": "计算机学院",
+          "deptCategory": "college",
+          "orderNum": 1,
+          "clickable": false,
+          "children": [
+            {
+              "deptId": 103,
+              "parentId": 102,
+              "deptName": "计算机科学与技术专业",
+              "deptCategory": "major",
+              "orderNum": 1,
+              "clickable": false,
+              "children": [
+                {
+                  "deptId": 104,
+                  "parentId": 103,
+                  "deptName": "计算机2023-1班",
+                  "deptCategory": "class",
+                  "orderNum": 1,
+                  "clickable": true,
+                  "children": []
+                },
+                {
+                  "deptId": 105,
+                  "parentId": 103,
+                  "deptName": "计算机2023-2班",
+                  "deptCategory": "class",
+                  "orderNum": 2,
+                  "clickable": true,
+                  "children": []
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}
+```
+
+#### 响应字段说明
+| 字段名 | 类型 | 说明 |
+|--------|------|------|
+| deptId | long | 部门ID |
+| parentId | long | 父部门ID |
+| deptName | string | 部门名称 |
+| deptCategory | string | 部门类别编码 |
+| orderNum | integer | 显示顺序 |
+| clickable | boolean | 是否可点击(只有班级节点可点击) |
+| children | array | 子部门列表 |
+
+#### 使用说明
+- 该接口无需传参,自动获取当前登录用户的权限范围
+- 返回的树形结构包含完整的层级关系,从根节点到叶子节点
+- `clickable` 字段标识节点是否可点击,只有班级节点(包含"班"字或dept_category包含"class")设置为true
+- 前端可根据 `clickable` 字段控制节点的交互行为
+- 树形结构按 `orderNum` 字段排序
+
+## 3. 数据字典
+
+### 3.1 权限类型 (permissionType)
+| 值 | 说明 |
+|----|------|
+| 1 | 查看 |
+| 2 | 编辑 |
+| 3 | 完全管理 |
+
+### 3.2 授权类型 (authType)
+| 值 | 说明 |
+|----|------|
+| 1 | 全校级 |
+| 2 | 自定义 |
+| 3 | 班主任 |
+
+### 3.3 状态 (status)
+| 值 | 说明 |
+|----|------|
+| 0 | 有效 |
+| 1 | 已撤销 |
+
+## 4. 权限说明
+
+### 4.1 数据权限控制
+- **超级管理员**: 可管理所有学生管理员授权关系
+- **学工管理员**: 可管理所有学生管理员授权关系
+- **二级学院管理员**: 只能管理本学院相关的授权关系
+- **普通用户**: 只能查看自己的权限信息
+
+### 4.2 操作权限控制
+- **查询**: 所有有相关权限的用户
+- **新增**: 学工管理员、二级学院管理员
+- **修改**: 学工管理员、二级学院管理员
+- **删除**: 学工管理员、二级学院管理员
+- **授权**: 学工管理员、二级学院管理员
+
+## 5. 注意事项
+
+1. 所有接口都需要Bearer Token认证
+2. 删除操作为逻辑删除,不会物理删除数据
+3. 授权关系具有层级性,上级权限包含下级权限
+4. 支持多租户数据隔离
+5. 获取当前用户权限树接口会自动过滤无权限的部门
+6. 部门树构建时会自动补全父节点层级关系
+
+---
+
+**文档更新时间**: 2025-07-24  
+**文档版本**: 1.0.0  
+**维护人员**: Cursor AI  
+**更新说明**: 新增学生管理员授权关系管理接口,包含获取当前用户权限部门树功能 

+ 259 - 0
docs/开发文档/API/宿舍管理员信息API.md

@@ -0,0 +1,259 @@
+# 宿舍管理员信息API文档
+
+## 1. API概述
+本文档提供宿舍管理员信息相关的API接口说明,包括管理员信息的查询、新增、修改、删除等功能,以及获取可用教师列表的接口。
+
+## 2. 基本信息
+- **模块名称**:宿舍管理员信息管理
+- **接口前缀**:`/xgStudentManager/studentManager`
+- **数据权限**:需要相应的权限才能访问接口
+
+## 3. API列表
+
+### 3.1 查询宿舍管理员信息列表
+
+- **接口地址**:`/xgStudentManager/studentManager/list`
+- **请求方式**:GET
+- **权限标识**:`xgStudentManager:studentManager:list`
+- **请求参数**:
+
+| 参数名      | 类型   | 是否必须 | 描述                                           |
+|------------|--------|----------|-----------------------------------------------|
+| managerName| string | 否       | 管理员姓名,支持模糊查询                         |
+| managerType| string | 否       | 管理员类型(1 全校管理员 2 自定义管理员 3 班主任) |
+| managerLevel| number | 否      | 管理级别(1-3,1为最高)                          |
+| phone      | string | 否       | 联系电话                                       |
+| gender     | string | 否       | 性别(0男 1女 2未知)                            |
+| status     | string | 否       | 状态(0正常 1停用)                              |
+| pageNum    | number | 是       | 页码                                          |
+| pageSize   | number | 是       | 每页记录数                                     |
+
+- **返回示例**:
+```json
+{
+  "total": 2,
+  "rows": [
+    {
+      "id": 1,
+      "managerName": "张三",
+      "managerType": "1",
+      "managerLevel": 1,
+      "userId": 103,
+      "phone": "13800138001",
+      "gender": "0",
+      "status": "0",
+      "remark": "全校管理员",
+      "createTime": "2025-07-24 10:00:00",
+      "updateTime": "2025-07-24 10:00:00"
+    }
+  ],
+  "code": 200,
+  "msg": "查询成功"
+}
+```
+
+### 3.2 获取宿舍管理员信息详细信息
+
+- **接口地址**:`/xgStudentManager/studentManager/{id}`
+- **请求方式**:GET
+- **权限标识**:`xgStudentManager:studentManager:query`
+- **请求参数**:
+
+| 参数名 | 类型   | 是否必须 | 描述   |
+|-------|--------|----------|-------|
+| id    | number | 是       | 主键ID |
+
+- **返回示例**:
+```json
+{
+  "data": {
+    "id": 1,
+    "managerName": "张三",
+    "managerType": "1",
+    "managerLevel": 1,
+    "userId": 103,
+    "phone": "13800138001",
+    "gender": "0",
+    "status": "0",
+    "remark": "全校管理员",
+    "createTime": "2025-07-24 10:00:00",
+    "updateTime": "2025-07-24 10:00:00"
+  },
+  "code": 200,
+  "msg": "操作成功"
+}
+```
+
+### 3.3 新增宿舍管理员信息
+
+- **接口地址**:`/xgStudentManager/studentManager`
+- **请求方式**:POST
+- **权限标识**:`xgStudentManager:studentManager:add`
+- **请求参数**:
+
+| 参数名      | 类型   | 是否必须 | 描述                                           |
+|------------|--------|----------|-----------------------------------------------|
+| managerName| string | 是       | 管理员姓名                                      |
+| managerType| string | 是       | 管理员类型(1 全校管理员 2 自定义管理员 3 班主任) |
+| managerLevel| number | 是      | 管理级别(1-3,1为最高)                          |
+| userId     | number | 是       | 关联用户ID                                     |
+| phone      | string | 是       | 联系电话                                       |
+| gender     | string | 是       | 性别(0男 1女 2未知)                            |
+| status     | string | 否       | 状态(0正常 1停用),默认0                        |
+| remark     | string | 否       | 备注                                           |
+
+- **返回示例**:
+```json
+{
+  "code": 200,
+  "msg": "操作成功"
+}
+```
+
+### 3.4 修改宿舍管理员信息
+
+- **接口地址**:`/xgStudentManager/studentManager`
+- **请求方式**:PUT
+- **权限标识**:`xgStudentManager:studentManager:edit`
+- **请求参数**:
+
+| 参数名      | 类型   | 是否必须 | 描述                                           |
+|------------|--------|----------|-----------------------------------------------|
+| id         | number | 是       | 主键ID                                         |
+| managerName| string | 是       | 管理员姓名                                      |
+| managerType| string | 是       | 管理员类型(1 全校管理员 2 自定义管理员 3 班主任) |
+| managerLevel| number | 是      | 管理级别(1-3,1为最高)                          |
+| userId     | number | 是       | 关联用户ID                                     |
+| phone      | string | 是       | 联系电话                                       |
+| gender     | string | 是       | 性别(0男 1女 2未知)                            |
+| status     | string | 否       | 状态(0正常 1停用)                              |
+| remark     | string | 否       | 备注                                           |
+
+- **返回示例**:
+```json
+{
+  "code": 200,
+  "msg": "操作成功"
+}
+```
+
+### 3.5 删除宿舍管理员信息
+
+- **接口地址**:`/xgStudentManager/studentManager/{ids}`
+- **请求方式**:DELETE
+- **权限标识**:`xgStudentManager:studentManager:remove`
+- **请求参数**:
+
+| 参数名 | 类型   | 是否必须 | 描述                   |
+|-------|--------|----------|-----------------------|
+| ids   | array  | 是       | 主键ID数组,格式:[1,2] |
+
+- **返回示例**:
+```json
+{
+  "code": 200,
+  "msg": "操作成功"
+}
+```
+
+### 3.6 导出宿舍管理员信息
+
+- **接口地址**:`/xgStudentManager/studentManager/export`
+- **请求方式**:POST
+- **权限标识**:`xgStudentManager:studentManager:export`
+- **请求参数**:与查询列表接口参数相同
+- **返回内容**:Excel文件
+
+### 3.7 获取可用教师列表
+
+- **接口地址**:`/xgStudentManager/studentManager/listAvailableTeachers`
+- **请求方式**:GET
+- **权限标识**:`xgStudentManager:studentManager:list`
+- **请求参数**:
+
+| 参数名      | 类型   | 是否必须 | 描述                                           |
+|------------|--------|----------|-----------------------------------------------|
+| managerType| string | 是       | 管理员类型(1 全校管理员 2 自定义管理员 3 班主任) |
+
+- **返回示例**:
+```json
+{
+  "data": [
+    {
+      "id": 101,
+      "managerName": "张三",
+      "phone": "13800138001",
+      "gender": "0"
+    },
+    {
+      "id": 102,
+      "managerName": "李四",
+      "phone": "13800138002",
+      "gender": "1"
+    }
+  ],
+  "code": 200,
+  "msg": "操作成功"
+}
+```
+
+## 获取当前正在教学的班主任信息
+
+- **接口路径**:`/xgStudentManager/studentManager/listActiveClassTeachers`
+- **请求方式**:GET
+- **权限标识**:`xgStudentManager:studentManager:list`
+- **入参**:
+  - `phone`(可选,string):手机号,模糊查询
+  - `name`(可选,string):姓名,模糊查询
+- **出参**:
+  - `name`:姓名
+  - `phone`:手机号
+  - `sex`:性别
+  - `className`:班级名称
+- **返回示例**:
+```json
+{
+  "code": 200,
+  "msg": "操作成功",
+  "data": [
+    {
+      "name": "张三",
+      "phone": "13800001111",
+      "sex": "0",
+      "className": "2025级1班"
+    }
+  ]
+}
+```
+- **说明**:
+  - 仅返回当前“正在教学”的班主任(班级状态为1,未删除,班主任ID有效,用户状态正常)。
+  - 支持姓名和手机号的模糊查询,均为可选参数。
+
+## 4. 数据结构说明
+
+### 4.1 宿舍管理员对象
+
+| 字段名      | 类型   | 描述                                           |
+|------------|--------|-----------------------------------------------|
+| id         | number | 主键ID                                         |
+| managerName| string | 管理员姓名                                      |
+| managerType| string | 管理员类型(1 全校管理员 2 自定义管理员 3 班主任) |
+| managerLevel| number | 管理级别(1-3,1为最高)                          |
+| userId     | number | 关联用户ID                                     |
+| phone      | string | 联系电话                                       |
+| gender     | string | 性别(0男 1女 2未知)                            |
+| status     | string | 状态(0正常 1停用)                              |
+| deleted    | string | 删除标志(0代表存在 1代表删除)                    |
+| remark     | string | 备注                                           |
+| createBy   | number | 创建者                                         |
+| createTime | date   | 创建时间                                       |
+| updateBy   | number | 更新者                                         |
+| updateTime | date   | 更新时间                                       |
+
+## 5. 错误码说明
+
+| 错误码 | 描述           | 解决方案                   |
+|-------|----------------|-----------------------------|
+| 500   | 系统内部错误    | 联系系统管理员               |
+| 401   | 未授权         | 检查用户权限或重新登录         |
+| 400   | 请求参数错误    | 检查请求参数是否符合要求      | 

+ 10 - 5
web/src/api/studentMsg/index.ts

@@ -9,12 +9,17 @@ import { StudentQuery, StudentVO, StudentForm, XgDeptTreeVO, R, TableDataInfo }
  */
 export const XgDeptTreeSelect = (belongDeptId?: number): AxiosPromise<XgDeptTreeVO[]> => {
   // 实际项目中连接后端接口
+  // return request({
+  //   url: '/system/user/deptTree',
+  //   method: 'get',
+  //   params: {
+  //     belongDeptId
+  //   }
+  // });
+
   return request({
-    url: '/system/user/deptTree',
-    method: 'get',
-    params: {
-      belongDeptId
-    }
+    url: '/xgStudentManagerSource/studentManagerSource/currentUserAuthTree',
+    method: 'get'
   });
 
   // // 模拟数据,直接返回与页面组件期望格式一致的数据

+ 97 - 0
web/src/api/studentMsg/types.ts

@@ -49,6 +49,7 @@ export interface StudentVO {
 
 // 学生表单对象
 export interface StudentForm {
+  // 原有字段
   studentId?: number | string;
   deptId?: number;
   studentNo: string;
@@ -64,6 +65,102 @@ export interface StudentForm {
   classId?: number;
   address?: string;
   remark?: string;
+
+  // 基础信息字段
+  student_no?: string;
+  id_card?: string;
+  name?: string;
+  name_english?: string;
+  former_name?: string;
+  gender?: string;
+  birth_date?: string;
+  nationality?: string;
+  native_place?: string;
+  political_status?: string;
+  hukou_location?: string;
+  home_address?: string;
+  current_address?: string;
+  postal_code?: string;
+  photo_url?: string;
+  photo_path?: string;
+  phone?: string;
+  is_boarder?: number;
+
+  // 扩展信息字段
+  education_level?: string;
+  household_register_type?: string;
+  religion?: string;
+  source_region?: string;
+  graduate_school?: string;
+  qq?: string;
+  wechat?: string;
+  marital_status?: string;
+  emergency_contact_name?: string;
+  emergency_contact_phone?: string;
+  emergency_contact_relation?: string;
+  is_poor_student?: number;
+  is_minority?: number;
+  is_disabled?: number;
+  is_orphan?: number;
+  is_left_behind_child?: number;
+  is_single_child?: number;
+  is_low_income?: number;
+  has_loan?: number;
+  loan_amount?: string;
+  specialties?: string;
+  awards?: string;
+  violations?: string;
+  social_activities?: string;
+
+  // 家庭信息字段
+  father_name?: string;
+  father_phone?: string;
+  father_work_unit?: string;
+  mother_name?: string;
+  mother_phone?: string;
+  mother_work_unit?: string;
+  guardian_name?: string;
+  guardian_phone?: string;
+  guardian_relation?: string;
+  family_income?: string;
+  per_capita_income?: string;
+  income_source?: string;
+  family_economic_status?: string;
+  family_members?: string;
+
+  // 健康信息字段
+  height?: string;
+  weight?: string;
+  blood_type?: string;
+  health_status?: string;
+  special_condition?: string;
+  medical_history?: string;
+  allergies?: string;
+  disability_type?: string;
+  disability_level?: string;
+
+  // 学籍信息字段
+  admission_category?: string;
+  student_status?: string;
+  enrollment_year?: string;
+  enrollment_date?: string;
+  school_system?: string;
+  academic_level?: string;
+  training_type?: string;
+  major_name?: string;
+  class_name?: string;
+  grade?: string;
+  college_name?: string;
+  counselor_name?: string;
+  class_teacher_name?: string;
+  dormitory_building?: string;
+  dormitory_room?: string;
+
+  // 银行卡信息字段
+  bank_card_no?: string;
+  bank_name?: string;
+  bank_branch?: string;
+  card_holder_name?: string;
 }
 
 // 统一响应类型

+ 95 - 0
web/src/api/xgStudentManager/studentManager/index.ts

@@ -0,0 +1,95 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { StudentManagerVO, StudentManagerForm, StudentManagerQuery } from '@/api/xgStudentManager/studentManager/types';
+
+// 临时定义一个接口来表示返回的教师数据
+export interface TeacherData {
+  id: string | number;
+  managerName: string;
+  phone: string;
+  gender: string;
+}
+
+/**
+ * 获取可用的教职工信息列表
+ * @param managerType 管理员类型(1 全校管理员 2 自定义管理员 3 班主任)
+ */
+export const listAvailableTeachers = (managerType: string): AxiosPromise<TeacherData[]> => {
+  return request({
+    url: '/xgStudentManager/studentManager/listAvailableTeachers',
+    method: 'get',
+    params: { managerType }
+  });
+};
+
+/**
+ * 查询宿舍管理员信息列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listStudentManager = (query?: StudentManagerQuery): AxiosPromise<StudentManagerVO[]> => {
+  return request({
+    url: '/xgStudentManager/studentManager/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询宿舍管理员信息详细
+ * @param id
+ */
+export const getStudentManager = (id: string | number): AxiosPromise<StudentManagerVO> => {
+  return request({
+    url: '/xgStudentManager/studentManager/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增宿舍管理员信息
+ * @param data
+ */
+export const addStudentManager = (data: StudentManagerForm) => {
+  return request({
+    url: '/xgStudentManager/studentManager',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改宿舍管理员信息
+ * @param data
+ */
+export const updateStudentManager = (data: StudentManagerForm) => {
+  return request({
+    url: '/xgStudentManager/studentManager',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除宿舍管理员信息
+ * @param id
+ */
+export const delStudentManager = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/xgStudentManager/studentManager/' + id,
+    method: 'delete'
+  });
+};
+
+/**
+ * 获取当前正在教学的班主任信息
+ * @param params 包含 phone 和 name 字段
+ */
+export const listActiveClassTeachers = (params: { phone?: string; name?: string }) => {
+  return request({
+    url: '/xgStudentManager/studentManager/listActiveClassTeachers',
+    method: 'get',
+    params
+  });
+};

+ 156 - 0
web/src/api/xgStudentManager/studentManager/types.ts

@@ -0,0 +1,156 @@
+export interface StudentManagerVO {
+  /**
+   * 主键ID
+   */
+  id: string | number;
+
+  /**
+   * 管理员姓名
+   */
+  managerName: string;
+
+  /**
+   * 管理员类型(1 全校管理员 2 自定义管理员 3 班主任)
+   */
+  managerType: string;
+
+  /**
+   * 管理级别(1-3,1为最高)
+   */
+  managerLevel: number;
+
+  /**
+   * 关联用户ID
+   */
+  userId: string | number;
+
+  /**
+   * 联系电话
+   */
+  phone: string;
+
+  /**
+   * 性别(0男 1女 2未知)
+   */
+  gender: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status: string;
+
+  /**
+   * 删除标志(0代表存在 1代表删除)
+   */
+  deleted: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface StudentManagerForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  id?: string | number;
+
+  /**
+   * 管理员姓名
+   */
+  managerName?: string;
+
+  /**
+   * 管理员类型(1 全校管理员 2 自定义管理员 3 班主任)
+   */
+  managerType?: string;
+
+  /**
+   * 管理级别(1-3,1为最高)
+   */
+  managerLevel?: number;
+
+  /**
+   * 关联用户ID
+   */
+  userId?: string | number;
+
+  /**
+   * 联系电话
+   */
+  phone?: string;
+
+  /**
+   * 性别(0男 1女 2未知)
+   */
+  gender?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 删除标志(0代表存在 1代表删除)
+   */
+  deleted?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface StudentManagerQuery extends PageQuery {
+
+  /**
+   * 管理员姓名
+   */
+  managerName?: string;
+
+  /**
+   * 管理员类型(1 全校管理员 2 自定义管理员 3 班主任)
+   */
+  managerType?: string;
+
+  /**
+   * 管理级别(1-3,1为最高)
+   */
+  managerLevel?: number;
+
+  /**
+   * 关联用户ID
+   */
+  userId?: string | number;
+
+  /**
+   * 联系电话
+   */
+  phone?: string;
+
+  /**
+   * 性别(0男 1女 2未知)
+   */
+  gender?: string;
+
+  /**
+   * 状态(0正常 1停用)
+   */
+  status?: string;
+
+  /**
+   * 删除标志(0代表存在 1代表删除)
+   */
+  deleted?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 63 - 0
web/src/api/xgStudentManagerSource/studentManagerSource/index.ts

@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { StudentManagerSourceVO, StudentManagerSourceForm, StudentManagerSourceQuery } from '@/api/xgStudentManagerSource/studentManagerSource/types';
+
+/**
+ * 查询学生管理员授权关系列表
+ * @param query
+ * @returns {*}
+ */
+
+export const listStudentManagerSource = (query?: StudentManagerSourceQuery): AxiosPromise<StudentManagerSourceVO[]> => {
+  return request({
+    url: '/xgStudentManagerSource/studentManagerSource/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 查询学生管理员授权关系详细
+ * @param id
+ */
+export const getStudentManagerSource = (id: string | number): AxiosPromise<StudentManagerSourceVO> => {
+  return request({
+    url: '/xgStudentManagerSource/studentManagerSource/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 新增学生管理员授权关系
+ * @param data
+ */
+export const addStudentManagerSource = (data: StudentManagerSourceForm) => {
+  return request({
+    url: '/xgStudentManagerSource/studentManagerSource',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改学生管理员授权关系
+ * @param data
+ */
+export const updateStudentManagerSource = (data: StudentManagerSourceForm) => {
+  return request({
+    url: '/xgStudentManagerSource/studentManagerSource',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 删除学生管理员授权关系
+ * @param id
+ */
+export const delStudentManagerSource = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/xgStudentManagerSource/studentManagerSource/' + id,
+    method: 'delete'
+  });
+};

+ 126 - 0
web/src/api/xgStudentManagerSource/studentManagerSource/types.ts

@@ -0,0 +1,126 @@
+export interface StudentManagerSourceVO {
+  /**
+   * 主键ID
+   */
+  id: string | number;
+
+  /**
+   * 管理员ID
+   */
+  managerId: string | number;
+
+  /**
+   * 权限类型(1查看 2编辑 3完全管理)
+   */
+  permissionType: string;
+
+  /**
+   * 授权类型(1全校级 2自定义 3班主任)
+   */
+  authType: string;
+
+  /**
+   * 授权来源ID(对应班级id)
+   */
+  authSource: number;
+
+  /**
+   * 状态(0有效 1已撤销)
+   */
+  status: string;
+
+  /**
+   * 删除标志(0代表存在 1代表删除)
+   */
+  deleted: string;
+
+  /**
+   * 备注
+   */
+  remark: string;
+
+}
+
+export interface StudentManagerSourceForm extends BaseEntity {
+  /**
+   * 主键ID
+   */
+  id?: string | number;
+
+  /**
+   * 管理员ID
+   */
+  managerId?: string | number;
+
+  /**
+   * 权限类型(1查看 2编辑 3完全管理)
+   */
+  permissionType?: string;
+
+  /**
+   * 授权类型(1全校级 2自定义 3班主任)
+   */
+  authType?: string;
+
+  /**
+   * 授权来源ID(对应班级id)
+   */
+  authSource?: number;
+
+  /**
+   * 状态(0有效 1已撤销)
+   */
+  status?: string;
+
+  /**
+   * 删除标志(0代表存在 1代表删除)
+   */
+  deleted?: string;
+
+  /**
+   * 备注
+   */
+  remark?: string;
+
+}
+
+export interface StudentManagerSourceQuery extends PageQuery {
+
+  /**
+   * 管理员ID
+   */
+  managerId?: string | number;
+
+  /**
+   * 权限类型(1查看 2编辑 3完全管理)
+   */
+  permissionType?: string;
+
+  /**
+   * 授权类型(1全校级 2自定义 3班主任)
+   */
+  authType?: string;
+
+  /**
+   * 授权来源ID(对应班级id)
+   */
+  authSource?: number;
+
+  /**
+   * 状态(0有效 1已撤销)
+   */
+  status?: string;
+
+  /**
+   * 删除标志(0代表存在 1代表删除)
+   */
+  deleted?: string;
+
+    /**
+     * 日期范围参数
+     */
+    params?: any;
+}
+
+
+

+ 178 - 24
web/src/views/studentMsg/authmsg/components/AdminDialog.vue

@@ -8,7 +8,21 @@
   >
     <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
       <el-form-item label="姓名" prop="name">
-        <el-input v-model="form.name" placeholder="请输入姓名" />
+        <el-select
+          v-model="form.name"
+          filterable
+          placeholder="请选择教师姓名"
+          @change="handleTeacherChange"
+          :disabled="mode === 'edit'"
+          style="width: 100%"
+        >
+          <el-option v-for="item in teacherOptions" :key="item.id" :label="item.managerName" :value="item.managerName">
+            <div>
+              <span> {{ item.managerName }} </span>
+              <span style="color: #8492a6; font-size: 13px"> ({{ item.phone }}) ({{ item.userId }}) </span>
+            </div>
+          </el-option>
+        </el-select>
       </el-form-item>
       <el-form-item label="手机号码" prop="phone">
         <el-input v-model="form.phone" placeholder="请输入手机号码" />
@@ -33,8 +47,68 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, watchEffect, computed } from 'vue';
-import { ElMessage, ElMessageBox } from 'element-plus';
+import { ref, reactive, watchEffect, computed, onMounted } from 'vue';
+import { ElMessage, type FormInstance } from 'element-plus';
+import request from '@/utils/request';
+
+/**
+ * 教师信息数据结构
+ */
+interface TeacherData {
+  /** 教师ID */
+  userId: string | number;
+  /** 管理员姓名 */
+  managerName: string;
+  /** 手机号码 */
+  phone: string;
+  /** 性别(0男 1女) */
+  gender: string;
+}
+
+/**
+ * 获取可用的教职工信息列表
+ * @param managerType 管理员类型(1 全校管理员 2 自定义管理员 3 班主任)
+ */
+const listAvailableTeachers = (managerType: string) => {
+  return request({
+    url: '/xgStudentManager/studentManager/listAvailableTeachers',
+    method: 'get',
+    params: { managerType }
+  });
+};
+
+/**
+ * 新增宿舍管理员信息
+ */
+const addStudentManager = (data: any) => {
+  return request({
+    url: '/xgStudentManager/studentManager',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 修改宿舍管理员信息
+ */
+const updateStudentManager = (data: any) => {
+  return request({
+    url: '/xgStudentManager/studentManager',
+    method: 'put',
+    data: data
+  });
+};
+
+// 在setup函数中引入这种方式提示弹窗
+const message = (type: 'success' | 'error' | 'info', text: string) => {
+  if (type === 'success') {
+    ElMessage.success(text);
+  } else if (type === 'error') {
+    ElMessage.error(text);
+  } else {
+    ElMessage.info(text);
+  }
+};
 
 const props = defineProps({
   modelValue: {
@@ -57,6 +131,9 @@ const props = defineProps({
 
 const emit = defineEmits(['update:modelValue', 'submit']);
 
+// 教师选项列表
+const teacherOptions = ref<TeacherData[]>([]);
+
 // 计算属性:对话框可见性
 const visible = computed({
   get: () => props.modelValue,
@@ -64,9 +141,10 @@ const visible = computed({
 });
 
 // 表单和校验
-const formRef = ref(null);
+const formRef = ref<FormInstance>();
 const form = reactive({
   id: null,
+  userId: null,
   name: '',
   phone: '',
   sex: 0,
@@ -74,27 +152,81 @@ const form = reactive({
 });
 
 // 表单校验规则
-const rules = reactive({
-  name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
+const rules = {
+  name: [{ required: true, message: '请选择姓名', trigger: 'change' }],
   phone: [
     { required: true, message: '请输入手机号码', trigger: 'blur' },
     { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }
   ]
-});
+};
+
+// 获取管理员类型对应的值
+const getManagerType = (type: string): string => {
+  switch (type) {
+    case 'school':
+      return '1'; // 全校管理员
+    case 'custom':
+      return '2'; // 自定义管理员
+    case 'teacher':
+      return '3'; // 班主任
+    default:
+      return '1';
+  }
+};
+
+// 加载可用教师列表
+const loadTeachers = async () => {
+  try {
+    const managerType = getManagerType(props.type);
+    const res = await listAvailableTeachers(managerType);
+    if (res.data) {
+      teacherOptions.value = res.data;
+    }
+  } catch (error) {
+    console.error('获取可用教师列表失败', error);
+  }
+};
+
+// 选择教师后的处理
+const handleTeacherChange = (value: string) => {
+  const selectedTeacher = teacherOptions.value.find((item) => item.managerName === value);
+  if (selectedTeacher) {
+    console.log(selectedTeacher.gender === '0');
+    // 自动填充手机号码和性别
+    form.phone = selectedTeacher.phone || '';
+    form.sex = selectedTeacher.gender === '0' ? 0 : 1; // 0男 1女
+    form.userId = selectedTeacher.userId;
+  }
+};
 
 // 监听传入的admin数据,更新表单
 watchEffect(() => {
   if (props.admin) {
     form.id = props.admin.id;
-    form.name = props.admin.name;
+    form.userId = props.admin.userId;
+    form.name = props.admin.managerName;
     form.phone = props.admin.phone;
-    form.sex = props.admin.sex;
+    form.sex = props.admin.gender === '0' ? 0 : 1;
     form.remark = props.admin.remark;
   }
 });
 
+// 监听对话框可见性变化,当打开时加载教师列表
+watchEffect(() => {
+  if (visible.value && props.mode === 'add') {
+    loadTeachers();
+  }
+});
+
+// 组件挂载时加载教师列表
+onMounted(() => {
+  if (props.mode === 'add') {
+    loadTeachers();
+  }
+});
+
 // 根据类型获取标签文本
-function getTypeLabel(type) {
+function getTypeLabel(type: string): string {
   switch (type) {
     case 'school':
       return '全校管理员';
@@ -108,21 +240,43 @@ function getTypeLabel(type) {
 }
 
 // 提交表单
-function submitForm() {
-  formRef.value?.validate((valid) => {
+async function submitForm() {
+  formRef.value?.validate(async (valid: boolean) => {
     if (valid) {
-      // 这里模拟保存
-      emit('submit', {
-        id: form.id,
-        name: form.name,
-        phone: form.phone,
-        sex: form.sex,
-        remark: form.remark,
-        scope: props.admin?.scope || ''
-      });
-
-      ElMessage.success('保存成功');
-      visible.value = false;
+      try {
+        const formData = {
+          id: form.id,
+          managerName: form.name,
+          userId: form.userId,
+          phone: form.phone,
+          gender: form.sex,
+          remark: form.remark,
+          status: '0',
+          // scope: props.admin?.scope || '',
+          managerType: getManagerType(props.type),
+          managerLevel: getManagerType(props.type)
+        };
+
+        let result;
+        if (props.mode === 'add') {
+          // 调用添加接口
+          result = await addStudentManager(formData);
+        } else {
+          // 调用更新接口
+          result = await updateStudentManager(formData);
+        }
+
+        if (result.code === 200) {
+          message('success', props.mode === 'add' ? '添加成功' : '更新成功');
+          // 通知父组件处理成功
+          emit('submit', formData);
+          visible.value = false;
+        } else {
+          message('error', result.msg || '操作失败');
+        }
+      } catch (error: any) {
+        message('error', '操作失败: ' + (error.message || '未知错误'));
+      }
     }
   });
 }

+ 161 - 55
web/src/views/studentMsg/authmsg/components/ScopeDialog.vue

@@ -2,17 +2,35 @@
   <el-dialog :title="getScopeDialogTitle(type)" v-model="visible" width="600px" append-to-body destroy-on-close>
     <el-form :model="form" label-width="100px">
       <el-form-item :label="getScopeName(type)">
-        <div v-if="type === 'custom'">
-          <el-checkbox-group v-model="form.selectedScopes">
-            <el-checkbox v-for="item in scopeOptions.custom" :key="item.id" :label="item.id">
-              {{ item.name }}
-            </el-checkbox>
-          </el-checkbox-group>
+        <div v-if="loading" class="loading-container">
+          <el-icon class="is-loading"><Loading /></el-icon>
+          <span>加载中...</span>
+        </div>
+        <div v-else-if="type === 'custom'">
+          <el-tree
+            :data="scopeOptions.custom"
+            show-checkbox
+            node-key="id"
+            :default-checked-keys="form.selectedScopes"
+            :props="{ label: 'label', children: 'children' }"
+            @check="onTreeCheck"
+            default-expand-all
+            style="
+              width: 100%;
+              min-width: 100%;
+              overflow-x: auto;
+              overflow-y: auto;
+              /* border: 1px solid #ebeef5; */
+              border-radius: 4px;
+              padding: 10px;
+              box-sizing: border-box;
+            "
+          />
         </div>
         <div v-else-if="type === 'teacher'">
           <el-checkbox-group v-model="form.selectedScopes">
             <el-checkbox v-for="item in scopeOptions.teacher" :key="item.id" :label="item.id">
-              {{ item.name }}
+              {{ item.label }}
             </el-checkbox>
           </el-checkbox-group>
         </div>
@@ -21,15 +39,18 @@
     <template #footer>
       <div class="dialog-footer">
         <el-button @click="cancel">取 消</el-button>
-        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button type="primary" @click="submitForm" :loading="loading">确 定</el-button>
       </div>
     </template>
   </el-dialog>
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, computed } from 'vue';
-import { ElMessage, ElMessageBox } from 'element-plus';
+import { ref, reactive, computed, watch, onMounted } from 'vue';
+import { ElMessage } from 'element-plus';
+import { Loading } from '@element-plus/icons-vue';
+import axios from 'axios';
+import request from '@/utils/request';
 
 const props = defineProps({
   modelValue: {
@@ -54,35 +75,107 @@ const visible = computed({
   set: (value) => emit('update:modelValue', value)
 });
 
-// 范围选项(模拟数据)
-const scopeOptions = {
-  custom: [
-    { id: 1, name: '高一年级' },
-    { id: 2, name: '高二年级' },
-    { id: 3, name: '高三年级' },
-    { id: 4, name: '初一年级' },
-    { id: 5, name: '初二年级' },
-    { id: 6, name: '初三年级' }
-  ],
-  teacher: [
-    { id: 101, name: '高一(1)班' },
-    { id: 102, name: '高一(2)班' },
-    { id: 103, name: '高一(3)班' },
-    { id: 104, name: '高二(1)班' },
-    { id: 105, name: '高二(2)班' },
-    { id: 106, name: '高三(1)班' },
-    { id: 107, name: '高三(2)班' }
-  ]
-};
+// 范围选项(从API获取)
+const scopeOptions = reactive({
+  custom: [] as any[],
+  teacher: [] as any[]
+});
+
+// 加载状态
+const loading = ref(false);
 
 // 表单数据
 const form = reactive({
   selectedScopes: [] as number[]
 });
 
+// 获取部门树数据
+async function fetchDeptTree() {
+  try {
+    loading.value = true;
+    const response = await request({
+      url: '/system/user/deptTree',
+      method: 'get',
+      params: {
+        belongDeptId: 101
+      }
+    });
+    if (response && response.code === 200) {
+      // custom类型直接使用树形结构
+      if (props.type === 'custom') {
+        scopeOptions.custom = response.data || [];
+      } else if (props.type === 'teacher') {
+        // teacher类型仍可用扁平化
+        scopeOptions.teacher = flattenTreeData(response.data || []);
+      }
+    }
+  } catch (error) {
+    console.error('获取部门树数据失败:', error);
+    ElMessage.error('获取范围选项失败');
+  } finally {
+    loading.value = false;
+  }
+}
+
+// 将树形数据扁平化
+function flattenTreeData(treeData: any[]): any[] {
+  const result: any[] = [];
+
+  function traverse(nodes: any[]) {
+    nodes.forEach((node) => {
+      result.push({
+        id: node.id,
+        label: node.label
+      });
+
+      if (node.children && node.children.length > 0) {
+        traverse(node.children);
+      }
+    });
+  }
+
+  traverse(treeData);
+  return result;
+}
+
+// 新增:获取历史范围数据
+async function fetchSelectedScopes(managerId: number | string) {
+  if (!managerId) return;
+  try {
+    const res = await request({
+      url: '/xgStudentManagerSource/studentManagerSource/list',
+      method: 'get',
+      params: { managerId }
+    });
+    if (res && res.code === 200) {
+      // 提取rows中的authSource字段作为已选范围ID
+      form.selectedScopes = (res.rows || []).map((item: any) => item.authSource);
+    }
+  } catch (e) {
+    form.selectedScopes = [];
+  }
+}
+
+// 监听对话框显示状态,自动加载数据
+watch(visible, async (newVal) => {
+  if (newVal) {
+    const managerId = props.admin?.managerId || props.admin?.id;
+    await fetchDeptTree();
+    await fetchSelectedScopes(managerId);
+  } else {
+    // 对话框关闭时清空选择
+    form.selectedScopes = [];
+  }
+});
+
+// el-tree勾选事件,确保能正确获取所有勾选节点的id
+function onTreeCheck(checkedNodes: any, info: { checkedKeys: any[]; halfCheckedKeys: any[] }) {
+  form.selectedScopes = info.checkedKeys;
+}
+
 // 获取范围对话框标题
 function getScopeDialogTitle(type: string) {
-  const name = props.admin?.name || '';
+  const name = props.admin?.managerName || '';
   switch (type) {
     case 'custom':
       return `为【${name}】分配管理范围`;
@@ -106,32 +199,33 @@ function getScopeName(type: string) {
 }
 
 // 提交表单
-function submitForm() {
+async function submitForm() {
   // 组装要提交的数据
   const selectedIds = form.selectedScopes;
-  let scopeName = '';
-
-  // 根据类型获取选中的名称
-  if (type.value === 'custom') {
-    scopeName = scopeOptions.custom
-      .filter((item) => selectedIds.includes(item.id))
-      .map((item) => item.name)
-      .join(', ');
-  } else if (type.value === 'teacher') {
-    scopeName = scopeOptions.teacher
-      .filter((item) => selectedIds.includes(item.id))
-      .map((item) => item.name)
-      .join(', ');
+  console.log(selectedIds);
+  const managerId = props.admin?.managerId || props.admin?.id; // 兼容不同字段
+  if (!managerId) {
+    ElMessage.error('管理员ID缺失,无法提交');
+    return;
+  }
+  loading.value = true;
+  try {
+    await request({
+      url: '/xgStudentManagerSource/studentManagerSource/grantDeptAuth',
+      method: 'post',
+      params: { managerId },
+      data: selectedIds
+    });
+    ElMessage.success('分配成功');
+    visible.value = false;
+    emit('submit', {
+      sourceIds: selectedIds
+    });
+  } catch (error) {
+    ElMessage.error('分配失败,请重试');
+  } finally {
+    loading.value = false;
   }
-
-  // 提交
-  emit('submit', {
-    sourceIds: selectedIds,
-    scopeName: scopeName
-  });
-
-  ElMessage.success('分配成功');
-  visible.value = false;
 }
 
 // 取消
@@ -145,7 +239,8 @@ const type = computed(() => props.type);
 // 暴露组件以修复导出问题
 defineExpose({
   submitForm,
-  cancel
+  cancel,
+  fetchDeptTree
 });
 </script>
 
@@ -162,4 +257,15 @@ defineExpose({
   margin-right: 15px;
   margin-bottom: 10px;
 }
+.loading-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 20px;
+  color: #909399;
+}
+.loading-container .el-icon {
+  margin-right: 8px;
+  font-size: 16px;
+}
 </style>

+ 140 - 87
web/src/views/studentMsg/authmsg/index.vue

@@ -8,7 +8,7 @@
             <el-card shadow="hover">
               <el-form :inline="true" :model="schoolSearchForm" class="search-form">
                 <el-form-item label="姓名">
-                  <el-input v-model="schoolSearchForm.name" placeholder="请输入姓名" clearable @keyup.enter="handleSearch('school')" />
+                  <el-input v-model="schoolSearchForm.managerName" placeholder="请输入姓名" clearable @keyup.enter="handleSearch('school')" />
                 </el-form-item>
                 <el-form-item label="手机号码">
                   <el-input v-model="schoolSearchForm.phone" placeholder="请输入手机号码" clearable @keyup.enter="handleSearch('school')" />
@@ -30,11 +30,11 @@
               </el-row>
             </template>
             <el-table :data="filteredAdmins('school')" border>
-              <el-table-column prop="name" label="姓名" align="center" />
+              <el-table-column prop="managerName" label="姓名" align="center" />
               <el-table-column prop="phone" label="手机号码" align="center" />
-              <el-table-column prop="sex" label="性别" align="center">
+              <el-table-column prop="gender" label="性别" align="center">
                 <template #default="scope">
-                  {{ scope.row.sex === 0 ? '男' : '女' }}
+                  {{ scope.row.gender == 0 ? '男' : '女' }}
                 </template>
               </el-table-column>
               <el-table-column prop="remark" label="备注" align="center" />
@@ -57,13 +57,13 @@
             <el-card shadow="hover">
               <el-form :inline="true" :model="customSearchForm" class="search-form">
                 <el-form-item label="姓名">
-                  <el-input v-model="customSearchForm.name" placeholder="请输入姓名" clearable @keyup.enter="handleSearch('custom')" />
+                  <el-input v-model="customSearchForm.managerName" placeholder="请输入姓名" clearable @keyup.enter="handleSearch('custom')" />
                 </el-form-item>
                 <el-form-item label="手机号码">
                   <el-input v-model="customSearchForm.phone" placeholder="请输入手机号码" clearable @keyup.enter="handleSearch('custom')" />
                 </el-form-item>
                 <el-form-item>
-                  <el-button type="primary" icon="Search" @click="handleSearch('custom')">搜索</el-button>
+                  <el-button type="primary" icon="Search" @click="fetchCustomAdminsWithScope('custom')">搜索</el-button>
                   <el-button icon="Refresh" @click="handleReset('custom')">重置</el-button>
                 </el-form-item>
               </el-form>
@@ -78,14 +78,30 @@
               </el-row>
             </template>
             <el-table :data="filteredAdmins('custom')" border>
-              <el-table-column prop="name" label="姓名" align="center" />
+              <el-table-column prop="managerName" label="姓名" align="center" />
               <el-table-column prop="phone" label="手机号码" align="center" />
-              <el-table-column prop="sex" label="性别" align="center">
+              <el-table-column prop="gender" label="性别" align="center">
                 <template #default="scope">
-                  {{ scope.row.sex === 0 ? '男' : '女' }}
+                  {{ scope.row.gender == 0 ? '男' : '女' }}
+                </template>
+              </el-table-column>
+              <el-table-column prop="scope" label="管理范围" align="center">
+                <template #default="scope">
+                  <el-tooltip
+                    v-if="scope.row.authSourceNames && scope.row.authSourceNames.length > 2"
+                    :content="scope.row.authSourceNames.join(',')"
+                    placement="top"
+                  >
+                    <span>
+                      {{ scope.row.authSourceNames.slice(0, 2).join(',')
+                      }}{{ scope.row.authSourceNames.length > 2 ? ' 等' + scope.row.authSourceNames.length + '个' : '' }}
+                    </span>
+                  </el-tooltip>
+                  <span v-else>
+                    {{ scope.row.authSourceNames?.join(',') || '-' }}
+                  </span>
                 </template>
               </el-table-column>
-              <el-table-column prop="scope" label="管理范围" align="center" />
               <el-table-column prop="remark" label="备注" align="center" />
               <el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
                 <template #default="scope">
@@ -109,7 +125,7 @@
             <el-card shadow="hover">
               <el-form :inline="true" :model="teacherSearchForm" class="search-form">
                 <el-form-item label="姓名">
-                  <el-input v-model="teacherSearchForm.name" placeholder="请输入姓名" clearable @keyup.enter="handleSearch('teacher')" />
+                  <el-input v-model="teacherSearchForm.managerName" placeholder="请输入姓名" clearable @keyup.enter="handleSearch('teacher')" />
                 </el-form-item>
                 <el-form-item label="手机号码">
                   <el-input v-model="teacherSearchForm.phone" placeholder="请输入手机号码" clearable @keyup.enter="handleSearch('teacher')" />
@@ -122,13 +138,13 @@
             </el-card>
           </div>
           <el-card shadow="never">
-            <template #header>
+            <!-- <template #header>
               <el-row :gutter="10" class="mb8">
                 <el-col :span="1.5">
                   <el-button type="primary" plain icon="Plus" @click="onAdd('teacher')">新增班主任</el-button>
                 </el-col>
               </el-row>
-            </template>
+            </template> -->
             <el-table :data="filteredAdmins('teacher')" border>
               <el-table-column prop="name" label="姓名" align="center" />
               <el-table-column prop="phone" label="手机号码" align="center" />
@@ -137,9 +153,9 @@
                   {{ scope.row.sex === 0 ? '男' : '女' }}
                 </template>
               </el-table-column>
-              <el-table-column prop="scope" label="所管理班级" align="center" />
+              <el-table-column prop="className" label="所管理班级" align="center" />
               <el-table-column prop="remark" label="备注" align="center" />
-              <el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
+              <!-- <el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
                 <template #default="scope">
                   <el-tooltip content="编辑" placement="top">
                     <el-button link type="primary" plain icon="Edit" @click="onEdit('teacher', scope.row)" />
@@ -151,7 +167,7 @@
                     <el-button link type="danger" plain icon="Delete" @click="onDelete('teacher', scope.row)" />
                   </el-tooltip>
                 </template>
-              </el-table-column>
+              </el-table-column> -->
             </el-table>
           </el-card>
         </el-tab-pane>
@@ -163,23 +179,35 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, onMounted } from 'vue';
+import { ref, reactive, onMounted, watch } from 'vue';
 import AdminDialog from './components/AdminDialog.vue';
 import ScopeDialog from './components/ScopeDialog.vue';
 import { ElMessage, ElMessageBox } from 'element-plus';
+import { listStudentManager, delStudentManager, listActiveClassTeachers } from '@/api/xgStudentManager/studentManager';
+import type { StudentManagerQuery } from '@/api/xgStudentManager/studentManager/types';
+import service from '@/utils/request';
 
 // 暴露组件以修复导出问题
 defineExpose({});
 
-// 导入改为使用模拟数据
-// import { getAdminList, deleteAdmin, assignScope, getScopeNames } from '@/api/studentMsg/authmsg';
-
 const activeTab = ref('school');
 
 // 各tab独立检索表单
-const schoolSearchForm = reactive({ name: '', phone: '' });
-const customSearchForm = reactive({ name: '', phone: '' });
-const teacherSearchForm = reactive({ name: '', phone: '' });
+const schoolSearchForm = reactive<StudentManagerQuery>({
+  managerName: '',
+  phone: '',
+  managerType: '1',
+  pageNum: 1,
+  pageSize: 1000
+});
+const customSearchForm = reactive<StudentManagerQuery>({
+  managerName: '',
+  phone: '',
+  managerType: '2',
+  pageNum: 1,
+  pageSize: 1000
+});
+const teacherSearchForm = reactive({ managerName: '', phone: '' });
 
 // 管理员数据
 const schoolAdmins = ref([]);
@@ -192,46 +220,38 @@ const currentAdmin = ref<any>(null);
 const scopeDialogVisible = ref(false);
 const scopeType = ref('');
 
-// 模拟数据
-const mockData = {
-  school: [
-    { id: 1, name: '张校长', phone: '13800138001', sex: 0, remark: '全校管理员' },
-    { id: 2, name: '李主任', phone: '13800138002', sex: 1, remark: '教务处负责人' }
-  ],
-  custom: [
-    { id: 3, name: '王组长', phone: '13800138003', sex: 0, scope: '高一年级', remark: '年级组长' },
-    { id: 4, name: '赵主管', phone: '13800138004', sex: 1, scope: '高二年级', remark: '年级负责人' }
-  ],
-  teacher: [
-    { id: 5, name: '刘老师', phone: '13800138005', sex: 0, scope: '高一(1)班', remark: '数学教师' },
-    { id: 6, name: '陈老师', phone: '13800138006', sex: 1, scope: '高一(2)班', remark: '语文教师' }
-  ]
-};
+// 班主任模拟数据 (预留,后续完善)
+const mockTeacherData = [
+  { id: 5, name: '刘老师', phone: '13800138005', sex: 0, scope: '高一(1)班', remark: '数学教师' },
+  { id: 6, name: '陈老师', phone: '13800138006', sex: 1, scope: '高一(2)班', remark: '语文教师' }
+];
 
 // 获取管理员列表
 async function fetchAdmins(type: string) {
-  // 实际项目中这里会调用API
-  // 此处使用模拟数据
   try {
-    // 模拟API检索过滤
     const searchForm = getSearchForm(type);
-    const filteredData = mockData[type].filter((item) => {
-      const nameMatch = !searchForm.name || item.name.includes(searchForm.name);
-      const phoneMatch = !searchForm.phone || item.phone.includes(searchForm.phone);
-      return nameMatch && phoneMatch;
-    });
 
-    // 更新对应类型的管理员数据
-    switch (type) {
-      case 'school':
-        schoolAdmins.value = filteredData;
-        break;
-      case 'custom':
-        customAdmins.value = filteredData;
-        break;
-      case 'teacher':
-        teacherAdmins.value = filteredData;
-        break;
+    if (type === 'school' || type === 'custom') {
+      // 调用API获取全校管理员或自定义管理员数据
+      const managerType = type === 'school' ? '1' : '2';
+      const res = await listStudentManager({
+        ...searchForm,
+        managerType
+      });
+      // 更新对应类型的管理员数据
+      if (type === 'school') {
+        schoolAdmins.value = res.rows || [];
+      } else {
+        customAdmins.value = res.rows || [];
+      }
+    } else if (type === 'teacher') {
+      // 通过API获取班主任数据
+      const params = {
+        phone: searchForm.phone,
+        name: searchForm.managerName // teacherSearchForm 实际只有 managerName 字段
+      };
+      const res = await listActiveClassTeachers(params);
+      teacherAdmins.value = res.data || [];
     }
   } catch (error) {
     console.error('获取管理员列表失败:', error);
@@ -252,9 +272,34 @@ function getSearchForm(type: string) {
   }
 }
 
+// 获取自定义管理员及其管理范围
+async function fetchCustomAdminsWithScope() {
+  await fetchAdmins('custom');
+  // 遍历 customAdmins,异步获取每个管理员的authSource
+  for (const admin of customAdmins.value) {
+    try {
+      const res = await service({
+        url: '/xgStudentManagerSource/studentManagerSource//authSourceNames',
+        method: 'get',
+        params: { managerId: admin.id }
+      });
+      admin.authSourceNames = res?.data || [];
+    } catch (e) {
+      admin.authSourceNames = [];
+    }
+  }
+}
+
+// 监听tab切换,切到custom时自动加载管理范围
+watch(activeTab, (val) => {
+  if (val === 'custom') {
+    fetchCustomAdminsWithScope();
+  }
+});
+
 onMounted(() => {
   fetchAdmins('school');
-  fetchAdmins('custom');
+  fetchCustomAdminsWithScope();
   fetchAdmins('teacher');
 });
 
@@ -274,17 +319,31 @@ function onEdit(type: string, admin: any) {
 
 async function onDelete(type: string, admin: any) {
   // 二次确认
-  ElMessageBox.confirm(`确定要删除管理员【${admin.name}】吗?`, '提示', {
+  ElMessageBox.confirm(`确定要删除管理员【${admin.name || admin.managerName}】吗?`, '提示', {
     confirmButtonText: '确定',
     cancelButtonText: '取消',
     type: 'warning'
   })
     .then(async () => {
       try {
-        // 模拟删除成功
-        mockData[type] = mockData[type].filter((item) => item.id !== admin.id);
-        ElMessage.success('删除成功');
-        fetchAdmins(type); // 删除后刷新
+        if (type === 'school' || type === 'custom') {
+          // 调用删除API
+          const res = await delStudentManager(admin.id);
+          if (res.code === 200) {
+            ElMessage.success('删除成功');
+            fetchAdmins(type);
+          } else {
+            ElMessage.error(res.msg || '删除失败');
+          }
+        } else {
+          // 班主任暂时使用模拟数据
+          ElMessage.success('删除成功');
+          mockTeacherData.splice(
+            mockTeacherData.findIndex((item) => item.id === admin.id),
+            1
+          );
+          fetchAdmins(type);
+        }
       } catch (e) {
         ElMessage.error('删除失败');
       }
@@ -301,36 +360,23 @@ function onScope(type: string, admin: any) {
 }
 
 function onDialogSubmit(admin: any) {
-  // 模拟添加或编辑操作
-  if (dialogMode.value === 'add') {
-    // 生成新ID
-    const newId = Math.max(...mockData[activeTab.value].map((item) => item.id), 0) + 1;
-    admin.id = newId;
-    mockData[activeTab.value].push(admin);
+  // 表单已在对话框组件内处理API调用
+  // 刷新列表数据
+  if (activeTab.value === 'custom') {
+    fetchCustomAdminsWithScope();
   } else {
-    // 编辑
-    const index = mockData[activeTab.value].findIndex((item) => item.id === admin.id);
-    if (index !== -1) {
-      mockData[activeTab.value][index] = admin;
-    }
+    fetchAdmins(activeTab.value);
   }
-
-  // 刷新列表数据
-  fetchAdmins(activeTab.value);
   dialogVisible.value = false;
 }
 
 async function onScopeSubmit(scope: any) {
   try {
-    // 模拟分配范围
-    const index = mockData[scopeType.value].findIndex((item) => item.id === currentAdmin.value.id);
-    if (index !== -1) {
-      mockData[scopeType.value][index].scope = scope.scopeName;
-    }
-
+    // 范围分配已在对话框组件内处理API调用
     // 成功后刷新数据并提示
     ElMessage.success('管理范围分配成功');
     fetchAdmins(scopeType.value);
+    fetchCustomAdminsWithScope();
     scopeDialogVisible.value = false;
   } catch (error) {
     ElMessage.error('管理范围分配失败');
@@ -353,6 +399,9 @@ function getListByType(type: string) {
 
 // 过滤
 function filteredAdmins(type: string) {
+  if (type === 'custom') {
+    return customAdmins.value;
+  }
   return getListByType(type).value;
 }
 
@@ -363,19 +412,23 @@ function handleSearch(type: string) {
 function handleReset(type: string) {
   switch (type) {
     case 'school':
-      schoolSearchForm.name = '';
+      schoolSearchForm.managerName = '';
       schoolSearchForm.phone = '';
       break;
     case 'custom':
-      customSearchForm.name = '';
+      customSearchForm.managerName = '';
       customSearchForm.phone = '';
       break;
     case 'teacher':
-      teacherSearchForm.name = '';
+      teacherSearchForm.managerName = '';
       teacherSearchForm.phone = '';
       break;
   }
-  fetchAdmins(type);
+  if (type === 'custom') {
+    fetchCustomAdminsWithScope();
+  } else {
+    fetchAdmins(type);
+  }
 }
 </script>
 

Разлика између датотеке није приказан због своје велике величине
+ 810 - 146
web/src/views/studentMsg/studentMsg/index.vue


+ 57 - 7
web/src/views/system/user/index.vue

@@ -1,13 +1,13 @@
 <template>
   <div class="p-2">
-    <el-row :gutter="20">
+    <div style="display: flex; gap: 16px">
       <!-- 部门树 -->
-      <el-col :lg="4" :xs="24" style="">
+      <div style="width: 300px">
         <el-card shadow="hover">
           <el-input v-model="deptName" placeholder="请输入部门名称" prefix-icon="Search" clearable />
           <el-tree
             ref="deptTreeRef"
-            class="mt-2"
+            class="mt-2 dept-tree"
             node-key="id"
             :data="deptOptions"
             :props="{ label: 'label', children: 'children' } as any"
@@ -18,8 +18,8 @@
             @node-click="handleNodeClick"
           />
         </el-card>
-      </el-col>
-      <el-col :lg="20" :xs="24">
+      </div>
+      <div style="flex: 1; min-width: 0; /* 防止内容撑开容器 */">
         <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
           <div v-show="showSearch" class="mb-[10px]">
             <el-card shadow="hover">
@@ -138,8 +138,8 @@
             @pagination="getList"
           />
         </el-card>
-      </el-col>
-    </el-row>
+      </div>
+    </div>
 
     <!-- 添加或修改用户配置对话框 -->
     <el-dialog ref="formDialogRef" v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body @close="closeDialog">
@@ -664,3 +664,53 @@ async function handleDeptChange(value: number | string) {
   form.value.postIds = [];
 }
 </script>
+
+<style scoped>
+/* 部门树组件样式 */
+.dept-tree {
+  height: 600px;
+  overflow-y: auto;
+  overflow-x: auto;
+  width: 270px; /* 固定宽度,超出时显示滚动条 */
+  min-width: 270px;
+}
+
+/* 强制覆盖el-tree的默认样式 */
+.dept-tree :deep(.el-tree-node) {
+  white-space: nowrap !important;
+  width: max-content !important;
+  min-width: max-content !important;
+}
+
+.dept-tree :deep(.el-tree-node__content) {
+  white-space: nowrap !important;
+  min-width: max-content !important;
+  width: max-content !important;
+  overflow: visible !important;
+  text-overflow: unset !important; /* 移除省略号 */
+  max-width: none !important;
+}
+
+.dept-tree :deep(.el-tree-node__label) {
+  white-space: nowrap !important;
+  display: inline-block !important;
+  flex-shrink: 0 !important;
+  max-width: none !important;
+  overflow: visible !important;
+  text-overflow: unset !important; /* 移除省略号效果 */
+  width: max-content !important;
+}
+
+.dept-tree :deep(.el-tree-node__expand-icon) {
+  flex-shrink: 0 !important;
+}
+
+/* 确保树容器内部元素都不被截断 */
+.dept-tree :deep(.el-tree-node__children) {
+  width: max-content !important;
+}
+
+.dept-tree :deep(.el-tree-node > .el-tree-node__children) {
+  width: max-content !important;
+}
+</style>

Неке датотеке нису приказане због велике количине промена