提交代码

This commit is contained in:
zlwu committed 2025-10-25 12:25:01 +08:00
1 parent 594d0c5df3
commit f89d747952
13 files changed
+2655

No files matched your search

@@ -0,0 +1,87 @@
package org.dromara.xg.controller;
import lombok.RequiredArgsConstructor;
import jakarta.validation.constraints.NotNull;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.xg.domain.vo.XgDormApplicationVo;
import org.dromara.xg.domain.vo.XgHolidayStayVo;
import org.dromara.xg.domain.vo.XgStudentLeaveVo;
import org.dromara.xg.domain.bo.XgDormApplicationBo;
import org.dromara.xg.domain.bo.XgHolidayStayBo;
import org.dromara.xg.domain.bo.XgStudentLeaveBo;
import org.dromara.xg.service.IXgStudentDynamicsService;
/**
* 学生动向信息管理(宿管员)
*
* @author zlwu
* @date 2025-01-15
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/studentDynamics")
public class XgStudentDynamicsController extends BaseController {
private final IXgStudentDynamicsService xgStudentDynamicsService;
/**
* 查询宿管员权限内的学生调宿退宿申请列表
*/
@SaCheckPermission("studentDynamics:dormApplication:list")
@GetMapping("/dormApplicationList")
public TableDataInfo<XgDormApplicationVo> getDormApplicationList(XgDormApplicationBo bo, PageQuery pageQuery) {
return xgStudentDynamicsService.queryDormApplicationPageList(bo, pageQuery);
}
/**
* 查询宿管员权限内的学生假期留宿信息列表
*/
@SaCheckPermission("studentDynamics:holidayStay:list")
@GetMapping("/holidayStayList")
public TableDataInfo<XgHolidayStayVo> getHolidayStayList(XgHolidayStayBo bo, PageQuery pageQuery) {
return xgStudentDynamicsService.queryHolidayStayPageList(bo, pageQuery);
}
/**
* 查询宿管员权限内的学生请假信息列表
*/
@SaCheckPermission("studentDynamics:studentLeave:list")
@GetMapping("/studentLeaveList")
public TableDataInfo<XgStudentLeaveVo> getStudentLeaveList(XgStudentLeaveBo bo, PageQuery pageQuery) {
return xgStudentDynamicsService.queryStudentLeavePageList(bo, pageQuery);
}
/**
* 获取调宿退宿申请详细信息
*/
@SaCheckPermission("studentDynamics:dormApplication:query")
@GetMapping("/dormApplication/{id}")
public R<XgDormApplicationVo> getDormApplicationInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(xgStudentDynamicsService.queryDormApplicationById(id));
}
/**
* 获取假期留宿详细信息
*/
@SaCheckPermission("studentDynamics:holidayStay:query")
@GetMapping("/holidayStay/{id}")
public R<XgHolidayStayVo> getHolidayStayInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(xgStudentDynamicsService.queryHolidayStayById(id));
}
/**
* 获取学生请假详细信息
*/
@SaCheckPermission("studentDynamics:studentLeave:query")
@GetMapping("/studentLeave/{id}")
public R<XgStudentLeaveVo> getStudentLeaveInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
return R.ok(xgStudentDynamicsService.queryStudentLeaveById(id));
}
}
@@ -0,0 +1,85 @@
package org.dromara.xg.mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 学生动向信息管理(宿管员)Mapper接口
*
* @author zlwu
* @date 2025-01-15
*/
public interface XgStudentDynamicsMapper {
/**
* 检查用户是否有宿舍管理权限
*
* @param userId 用户ID
* @return 管理员类型列表 (1全校管理员 2校区管理员 3宿舍区管理员 4宿舍楼管理员)
*/
List<String> checkUserDormManagerType(@Param("userId") Long userId);
/**
* 获取全校管理员权限的所有楼栋ID列表
*
* @param userId 用户ID
* @return 楼栋ID列表
*/
List<Long> getBuildingIdsBySchoolAdmin(@Param("userId") Long userId);
/**
* 获取校区管理员权限的楼栋ID列表
*
* @param userId 用户ID
* @return 楼栋ID列表
*/
List<Long> getBuildingIdsByCampusAdmin(@Param("userId") Long userId);
/**
* 获取宿舍区管理员权限的楼栋ID列表
*
* @param userId 用户ID
* @return 楼栋ID列表
*/
List<Long> getBuildingIdsByAreaAdmin(@Param("userId") Long userId);
/**
* 获取宿舍楼管理员权限的楼栋ID列表
*
* @param userId 用户ID
* @return 楼栋ID列表
*/
List<Long> getBuildingIdsByBuildingAdmin(@Param("userId") Long userId);
/**
* 获取全校管理员权限的所有学生ID列表
*
* @param userId 用户ID
* @return 学生ID列表
*/
List<Long> getStudentIdsBySchoolAdmin(@Param("userId") Long userId);
/**
* 获取校区管理员权限的学生ID列表
*
* @param userId 用户ID
* @return 学生ID列表
*/
List<Long> getStudentIdsByCampusAdmin(@Param("userId") Long userId);
/**
* 获取宿舍区管理员权限的学生ID列表
*
* @param userId 用户ID
* @return 学生ID列表
*/
List<Long> getStudentIdsByAreaAdmin(@Param("userId") Long userId);
/**
* 获取宿舍楼管理员权限的学生ID列表
*
* @param userId 用户ID
* @return 学生ID列表
*/
List<Long> getStudentIdsByBuildingAdmin(@Param("userId") Long userId);
}
@@ -0,0 +1,49 @@
package org.dromara.xg.service;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.xg.domain.vo.XgDormApplicationVo;
import org.dromara.xg.domain.vo.XgHolidayStayVo;
import org.dromara.xg.domain.vo.XgStudentLeaveVo;
import org.dromara.xg.domain.bo.XgDormApplicationBo;
import org.dromara.xg.domain.bo.XgHolidayStayBo;
import org.dromara.xg.domain.bo.XgStudentLeaveBo;
/**
* 学生动向信息管理(宿管员)Service接口
*
* @author zlwu
* @date 2025-01-15
*/
public interface IXgStudentDynamicsService {
/**
* 查询宿管员权限内的学生调宿退宿申请列表
*/
TableDataInfo<XgDormApplicationVo> queryDormApplicationPageList(XgDormApplicationBo bo, PageQuery pageQuery);
/**
* 查询宿管员权限内的学生假期留宿信息列表
*/
TableDataInfo<XgHolidayStayVo> queryHolidayStayPageList(XgHolidayStayBo bo, PageQuery pageQuery);
/**
* 查询宿管员权限内的学生请假信息列表
*/
TableDataInfo<XgStudentLeaveVo> queryStudentLeavePageList(XgStudentLeaveBo bo, PageQuery pageQuery);
/**
* 根据主键查询调宿退宿申请详情
*/
XgDormApplicationVo queryDormApplicationById(Long id);
/**
* 根据主键查询假期留宿详情
*/
XgHolidayStayVo queryHolidayStayById(Long id);
/**
* 根据主键查询学生请假详情
*/
XgStudentLeaveVo queryStudentLeaveById(Long id);
}
@@ -0,0 +1,329 @@
package org.dromara.xg.service.impl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.xg.domain.vo.XgDormApplicationVo;
import org.dromara.xg.domain.vo.XgHolidayStayVo;
import org.dromara.xg.domain.vo.XgStudentLeaveVo;
import org.dromara.xg.domain.bo.XgDormApplicationBo;
import org.dromara.xg.domain.bo.XgHolidayStayBo;
import org.dromara.xg.domain.bo.XgStudentLeaveBo;
import org.dromara.xg.domain.XgDormApplication;
import org.dromara.xg.domain.XgHolidayStay;
import org.dromara.xg.domain.XgStudentLeave;
import org.dromara.xg.mapper.XgDormApplicationMapper;
import org.dromara.xg.mapper.XgHolidayStayMapper;
import org.dromara.xg.mapper.XgStudentLeaveMapper;
import org.dromara.xg.mapper.XgStudentDynamicsMapper;
import org.dromara.xg.service.IXgStudentDynamicsService;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 学生动向信息管理(宿管员)Service实现
*
* @author zlwu
* @date 2025-01-15
*/
@RequiredArgsConstructor
@Service
public class XgStudentDynamicsServiceImpl implements IXgStudentDynamicsService {
private final XgDormApplicationMapper dormApplicationMapper;
private final XgHolidayStayMapper holidayStayMapper;
private final XgStudentLeaveMapper studentLeaveMapper;
private final XgStudentDynamicsMapper studentDynamicsMapper;
/**
* 查询宿管员权限内的学生调宿退宿申请列表
*/
@Override
public TableDataInfo<XgDormApplicationVo> queryDormApplicationPageList(XgDormApplicationBo bo,
PageQuery pageQuery) {
// 获取当前登录用户ID
Long currentUserId = LoginHelper.getUserId();
// 构建查询条件
LambdaQueryWrapper<XgDormApplication> lqw = buildDormApplicationQueryWrapper(bo, currentUserId);
// 分页查询
Page<XgDormApplicationVo> result = dormApplicationMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询宿管员权限内的学生假期留宿信息列表
*/
@Override
public TableDataInfo<XgHolidayStayVo> queryHolidayStayPageList(XgHolidayStayBo bo, PageQuery pageQuery) {
// 获取当前登录用户ID
Long currentUserId = LoginHelper.getUserId();
// 构建查询条件
LambdaQueryWrapper<XgHolidayStay> lqw = buildHolidayStayQueryWrapper(bo, currentUserId);
// 分页查询
Page<XgHolidayStayVo> result = holidayStayMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 查询宿管员权限内的学生请假信息列表
*/
@Override
public TableDataInfo<XgStudentLeaveVo> queryStudentLeavePageList(XgStudentLeaveBo bo, PageQuery pageQuery) {
// 获取当前登录用户ID
Long currentUserId = LoginHelper.getUserId();
// 构建查询条件
LambdaQueryWrapper<XgStudentLeave> lqw = buildStudentLeaveQueryWrapper(bo, currentUserId);
// 分页查询
Page<XgStudentLeaveVo> result = studentLeaveMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* 根据主键查询调宿退宿申请详情
*/
@Override
public XgDormApplicationVo queryDormApplicationById(Long id) {
return dormApplicationMapper.selectVoById(id);
}
/**
* 根据主键查询假期留宿详情
*/
@Override
public XgHolidayStayVo queryHolidayStayById(Long id) {
return holidayStayMapper.selectVoById(id);
}
/**
* 根据主键查询学生请假详情
*/
@Override
public XgStudentLeaveVo queryStudentLeaveById(Long id) {
return studentLeaveMapper.selectVoById(id);
}
/**
* 构建调宿退宿申请查询条件(带宿管员权限)
*/
private LambdaQueryWrapper<XgDormApplication> buildDormApplicationQueryWrapper(XgDormApplicationBo bo,
Long currentUserId) {
LambdaQueryWrapper<XgDormApplication> lqw = new LambdaQueryWrapper<>();
// 获取宿管员管理的楼栋ID列表
List<Long> managedBuildingIds = getManagedBuildingIds(currentUserId);
if (managedBuildingIds.isEmpty()) {
// 如果没有管理权限,返回空结果
lqw.eq(XgDormApplication::getId, -1);
return lqw;
}
// 只查询宿管员权限内楼栋的学生申请
lqw.in(XgDormApplication::getCurrentBuildingId, managedBuildingIds);
// 添加其他查询条件
lqw.like(bo.getStudentName() != null, XgDormApplication::getStudentName, bo.getStudentName());
lqw.like(bo.getStudentNo() != null, XgDormApplication::getStudentNo, bo.getStudentNo());
lqw.like(bo.getCollegeName() != null, XgDormApplication::getCollegeName, bo.getCollegeName());
lqw.like(bo.getMajorName() != null, XgDormApplication::getMajorName, bo.getMajorName());
lqw.like(bo.getClassName() != null, XgDormApplication::getClassName, bo.getClassName());
lqw.eq(bo.getApplicationType() != null, XgDormApplication::getApplicationType, bo.getApplicationType());
lqw.eq(bo.getApplicationStatus() != null, XgDormApplication::getApplicationStatus, bo.getApplicationStatus());
lqw.ge(bo.getApplyTime() != null, XgDormApplication::getApplyTime, bo.getApplyTime());
// 默认按创建时间倒序
lqw.orderByDesc(XgDormApplication::getCreateTime);
return lqw;
}
/**
* 构建假期留宿查询条件(带宿管员权限)
*/
private LambdaQueryWrapper<XgHolidayStay> buildHolidayStayQueryWrapper(XgHolidayStayBo bo, Long currentUserId) {
LambdaQueryWrapper<XgHolidayStay> lqw = new LambdaQueryWrapper<>();
// 获取宿管员管理的楼栋ID列表
List<Long> managedBuildingIds = getManagedBuildingIds(currentUserId);
if (managedBuildingIds.isEmpty()) {
// 如果没有管理权限,返回空结果
lqw.eq(XgHolidayStay::getId, -1);
return lqw;
}
// 只查询宿管员权限内楼栋的学生留宿
lqw.in(XgHolidayStay::getBuildingId, managedBuildingIds);
// 添加其他查询条件
lqw.like(bo.getStudentName() != null, XgHolidayStay::getStudentName, bo.getStudentName());
lqw.like(bo.getStudentNo() != null, XgHolidayStay::getStudentNo, bo.getStudentNo());
lqw.like(bo.getCollegeName() != null, XgHolidayStay::getCollegeName, bo.getCollegeName());
lqw.like(bo.getMajorName() != null, XgHolidayStay::getMajorName, bo.getMajorName());
lqw.like(bo.getClassName() != null, XgHolidayStay::getClassName, bo.getClassName());
lqw.eq(bo.getHolidayType() != null, XgHolidayStay::getHolidayType, bo.getHolidayType());
lqw.ge(bo.getApplyStartDate() != null, XgHolidayStay::getApplyStartDate, bo.getApplyStartDate());
lqw.le(bo.getApplyEndDate() != null, XgHolidayStay::getApplyEndDate, bo.getApplyEndDate());
// 默认按创建时间倒序
lqw.orderByDesc(XgHolidayStay::getCreateTime);
return lqw;
}
/**
* 构建学生请假查询条件(带宿管员权限)
*/
private LambdaQueryWrapper<XgStudentLeave> buildStudentLeaveQueryWrapper(XgStudentLeaveBo bo, Long currentUserId) {
LambdaQueryWrapper<XgStudentLeave> lqw = new LambdaQueryWrapper<>();
// 获取宿管员权限内的学生ID列表
List<Long> managedStudentIds = getManagedStudentIds(currentUserId);
if (managedStudentIds.isEmpty()) {
// 如果没有管理权限,返回空结果
lqw.eq(XgStudentLeave::getId, -1);
return lqw;
}
// 只查询宿管员权限内的学生请假
lqw.in(XgStudentLeave::getStudentId, managedStudentIds);
// 添加其他查询条件
lqw.like(bo.getStudentName() != null, XgStudentLeave::getStudentName, bo.getStudentName());
lqw.like(bo.getStudentNo() != null, XgStudentLeave::getStudentNo, bo.getStudentNo());
lqw.like(bo.getCollegeName() != null, XgStudentLeave::getCollegeName, bo.getCollegeName());
lqw.like(bo.getMajorName() != null, XgStudentLeave::getMajorName, bo.getMajorName());
lqw.like(bo.getClassName() != null, XgStudentLeave::getClassName, bo.getClassName());
lqw.like(bo.getLeaveTypeName() != null, XgStudentLeave::getLeaveTypeName, bo.getLeaveTypeName());
lqw.eq(bo.getApprovalStatus() != null, XgStudentLeave::getApprovalStatus, bo.getApprovalStatus());
lqw.ge(bo.getStartTime() != null, XgStudentLeave::getStartTime, bo.getStartTime());
lqw.le(bo.getEndTime() != null, XgStudentLeave::getEndTime, bo.getEndTime());
// 默认按创建时间倒序
lqw.orderByDesc(XgStudentLeave::getCreateTime);
return lqw;
}
/**
* 获取宿管员管理的楼栋ID列表(支持多级权限合并)
*/
private List<Long> getManagedBuildingIds(Long userId) {
// 查询用户管理权限类型
List<String> managerTypes = studentDynamicsMapper.checkUserDormManagerType(userId);
if (managerTypes == null || managerTypes.isEmpty()) {
// 没有宿舍管理权限,返回空列表
return new ArrayList<>();
}
// 存储所有权限的楼栋ID,使用Set去重
Set<Long> buildingIdSet = new HashSet<>();
// 按照权限分别查询并合并结果(支持用户拥有多种角色的情况)
if (managerTypes.contains("1")) {
// 全校管理员,获取所有楼栋
List<Long> allBuildings = studentDynamicsMapper.getBuildingIdsBySchoolAdmin(userId);
if (allBuildings != null) {
buildingIdSet.addAll(allBuildings);
}
// 全校管理员可以查看所有楼栋,直接返回
return new ArrayList<>(buildingIdSet);
}
// 对于非全校管理员,合并多种权限的结果
if (managerTypes.contains("2")) {
// 校区管理员,获取管理的校区下的楼栋
List<Long> campusBuildings = studentDynamicsMapper.getBuildingIdsByCampusAdmin(userId);
if (campusBuildings != null) {
buildingIdSet.addAll(campusBuildings);
}
}
if (managerTypes.contains("3")) {
// 宿舍区管理员,获取管理的宿舍区下的楼栋
List<Long> areaBuildings = studentDynamicsMapper.getBuildingIdsByAreaAdmin(userId);
if (areaBuildings != null) {
buildingIdSet.addAll(areaBuildings);
}
}
if (managerTypes.contains("4")) {
// 宿舍楼管理员,获取管理的楼栋
List<Long> directBuildings = studentDynamicsMapper.getBuildingIdsByBuildingAdmin(userId);
if (directBuildings != null) {
buildingIdSet.addAll(directBuildings);
}
}
return new ArrayList<>(buildingIdSet);
}
/**
* 获取宿管员权限内的学生ID列表(支持多级权限合并)
*/
private List<Long> getManagedStudentIds(Long userId) {
// 查询用户管理权限类型
List<String> managerTypes = studentDynamicsMapper.checkUserDormManagerType(userId);
if (managerTypes == null || managerTypes.isEmpty()) {
// 没有宿舍管理权限,返回空列表
return new ArrayList<>();
}
// 存储所有权限的学生ID,使用Set去重
Set<Long> studentIdSet = new HashSet<>();
// 按照权限分别查询并合并结果(支持用户拥有多种角色的情况)
if (managerTypes.contains("1")) {
// 全校管理员,获取所有学生
List<Long> allStudents = studentDynamicsMapper.getStudentIdsBySchoolAdmin(userId);
if (allStudents != null) {
studentIdSet.addAll(allStudents);
}
// 全校管理员可以查看所有学生,直接返回
return new ArrayList<>(studentIdSet);
}
// 对于非全校管理员,合并多种权限的结果
if (managerTypes.contains("2")) {
// 校区管理员,获取管理的校区下的学生
List<Long> campusStudents = studentDynamicsMapper.getStudentIdsByCampusAdmin(userId);
if (campusStudents != null) {
studentIdSet.addAll(campusStudents);
}
}
if (managerTypes.contains("3")) {
// 宿舍区管理员,获取管理的宿舍区下的学生
List<Long> areaStudents = studentDynamicsMapper.getStudentIdsByAreaAdmin(userId);
if (areaStudents != null) {
studentIdSet.addAll(areaStudents);
}
}
if (managerTypes.contains("4")) {
// 宿舍楼管理员,获取管理的楼栋下的学生
List<Long> buildingStudents = studentDynamicsMapper.getStudentIdsByBuildingAdmin(userId);
if (buildingStudents != null) {
studentIdSet.addAll(buildingStudents);
}
}
return new ArrayList<>(studentIdSet);
}
}
@@ -0,0 +1,213 @@
<?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.XgStudentDynamicsMapper">
<!-- 检查用户是否有宿舍管理权限 -->
<select id="checkUserDormManagerType" resultType="String">
SELECT
DISTINCT manager_type
FROM
xg_dorm_manager
WHERE
user_id = #{userId}
AND deleted = '0'
AND status = '0'
</select>
<!-- 获取全校管理员权限的所有楼栋ID列表 -->
<select id="getBuildingIdsBySchoolAdmin" resultType="Long">
SELECT
id
FROM
xg_dorm_building
WHERE
deleted = '0'
AND EXISTS (
SELECT 1 FROM xg_dorm_manager m
WHERE m.user_id = #{userId}
AND m.manager_type = '1'
AND m.deleted = '0'
AND m.status = '0'
)
</select>
<!-- 获取校区管理员权限的楼栋ID列表 -->
<select id="getBuildingIdsByCampusAdmin" resultType="Long">
SELECT
b.id
FROM
xg_dorm_building b
WHERE
b.deleted = '0'
AND b.campus_id IN (
SELECT
s.auth_source
FROM
xg_dorm_manager m
JOIN
xg_dorm_manager_source s ON m.id = s.manager_id
WHERE
m.user_id = #{userId}
AND m.manager_type = '2'
AND m.deleted = '0'
AND m.status = '0'
AND s.deleted = '0'
AND s.status = '0'
)
</select>
<!-- 获取宿舍区管理员权限的楼栋ID列表 -->
<select id="getBuildingIdsByAreaAdmin" resultType="Long">
SELECT
b.id
FROM
xg_dorm_building b
WHERE
b.deleted = '0'
AND b.area_id IN (
SELECT
s.auth_source
FROM
xg_dorm_manager m
JOIN
xg_dorm_manager_source s ON m.id = s.manager_id
WHERE
m.user_id = #{userId}
AND m.manager_type = '3'
AND m.deleted = '0'
AND m.status = '0'
AND s.deleted = '0'
AND s.status = '0'
)
</select>
<!-- 获取宿舍楼管理员权限的楼栋ID列表 -->
<select id="getBuildingIdsByBuildingAdmin" resultType="Long">
SELECT
b.id
FROM
xg_dorm_building b
WHERE
b.deleted = '0'
AND b.id IN (
SELECT
s.auth_source
FROM
xg_dorm_manager m
JOIN
xg_dorm_manager_source s ON m.id = s.manager_id
WHERE
m.user_id = #{userId}
AND m.manager_type = '4'
AND m.deleted = '0'
AND m.status = '0'
AND s.deleted = '0'
AND s.status = '0'
)
</select>
<!-- 获取全校管理员权限的所有学生ID列表 -->
<select id="getStudentIdsBySchoolAdmin" resultType="Long">
SELECT
sm.id
FROM
xg_student_msg sm
WHERE
sm.deleted = 0
AND EXISTS (
SELECT 1 FROM xg_dorm_manager m
WHERE m.user_id = #{userId}
AND m.manager_type = '1'
AND m.deleted = '0'
AND m.status = '0'
)
</select>
<!-- 获取校区管理员权限的学生ID列表 -->
<select id="getStudentIdsByCampusAdmin" resultType="Long">
SELECT
sm.id
FROM
xg_student_msg sm
INNER JOIN
xg_dorm_building db ON sm.dormitory_building = db.id
WHERE
sm.deleted = 0
AND db.deleted = '0'
AND db.campus_id IN (
SELECT
s.auth_source
FROM
xg_dorm_manager m
JOIN
xg_dorm_manager_source s ON m.id = s.manager_id
WHERE
m.user_id = #{userId}
AND m.manager_type = '2'
AND m.deleted = '0'
AND m.status = '0'
AND s.deleted = '0'
AND s.status = '0'
)
</select>
<!-- 获取宿舍区管理员权限的学生ID列表 -->
<select id="getStudentIdsByAreaAdmin" resultType="Long">
SELECT
sm.id
FROM
xg_student_msg sm
INNER JOIN
xg_dorm_building db ON sm.dormitory_building = db.id
WHERE
sm.deleted = 0
AND db.deleted = '0'
AND db.area_id IN (
SELECT
s.auth_source
FROM
xg_dorm_manager m
JOIN
xg_dorm_manager_source s ON m.id = s.manager_id
WHERE
m.user_id = #{userId}
AND m.manager_type = '3'
AND m.deleted = '0'
AND m.status = '0'
AND s.deleted = '0'
AND s.status = '0'
)
</select>
<!-- 获取宿舍楼管理员权限的学生ID列表 -->
<select id="getStudentIdsByBuildingAdmin" resultType="Long">
SELECT
sm.id
FROM
xg_student_msg sm
INNER JOIN
xg_dorm_building db ON sm.dormitory_building = db.id
WHERE
sm.deleted = 0
AND db.deleted = '0'
AND db.id IN (
SELECT
s.auth_source
FROM
xg_dorm_manager m
JOIN
xg_dorm_manager_source s ON m.id = s.manager_id
WHERE
m.user_id = #{userId}
AND m.manager_type = '4'
AND m.deleted = '0'
AND m.status = '0'
AND s.deleted = '0'
AND s.status = '0'
)
</select>
</mapper>
+269
View File
@@ -0,0 +1,269 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
/**
* 学生动向信息管理(宿管员)API
* 这些接口专门用于学生动向Tab页,会自动根据宿管员权限过滤数据
*/
// ==================== 调宿退宿申请相关接口 ====================
export interface DormApplicationQuery {
pageNum?: number;
pageSize?: number;
studentName?: string;
studentNo?: string;
collegeName?: string;
majorName?: string;
className?: string;
applicationType?: string;
applicationStatus?: string;
applyTime?: string;
params?: any;
}
export interface DormApplicationVO {
id: string | number;
applicationName: string;
applicationType: string;
applicationStatus: string;
studentId: string | number;
studentNo: string;
studentName: string;
studentPhone: string;
collegeId: string | number;
collegeName: string;
majorId: string | number;
majorName: string;
classId: string | number;
className: string;
currentCampusId: string | number;
currentCampusName: string;
currentBuildingId: string | number;
currentBuildingName: string;
currentRoomId: string | number;
currentRoomNo: string;
currentBedId: string | number;
currentBedNo: string;
targetCampusId?: string | number;
targetCampusName?: string;
targetBuildingId?: string | number;
targetBuildingName?: string;
targetRoomId?: string | number;
targetRoomNo?: string;
targetBedId?: string | number;
targetBedNo?: string;
reasonDetail: string;
applyTime: string;
attachmentOssIds?: string;
remark?: string;
createTime: string;
updateTime: string;
}
/**
* 查询宿管员权限内的学生调宿退宿申请列表
*/
export function listDormApplication(query?: DormApplicationQuery): AxiosPromise<any> {
return request({
url: '/studentDynamics/dormApplicationList',
method: 'get',
params: query
});
}
/**
* 获取调宿退宿申请详细信息
*/
export function getDormApplication(id: string | number): AxiosPromise<DormApplicationVO> {
return request({
url: '/studentDynamics/dormApplication/' + id,
method: 'get'
});
}
/**
* 获取调宿退宿申请详细信息(别名)
*/
export const getDormApplicationInfo = getDormApplication;
/**
* 修改调宿退宿申请信息
*/
export function updateDormApplication(data: DormApplicationVO): AxiosPromise<any> {
return request({
url: '/studentDynamics/dormApplication',
method: 'put',
data: data
});
}
/**
* 删除调宿退宿申请
*/
export function delDormApplication(id: string | number | Array<string | number>): AxiosPromise<any> {
return request({
url: '/studentDynamics/dormApplication/' + id,
method: 'delete'
});
}
// ==================== 假期留宿相关接口 ====================
export interface HolidayStayQuery {
pageNum?: number;
pageSize?: number;
studentName?: string;
studentNo?: string;
collegeName?: string;
majorName?: string;
className?: string;
holidayType?: string;
applyStartDate?: string;
applyEndDate?: string;
status?: string;
params?: any;
}
export interface HolidayStayVO {
id: string | number;
studentId: string | number;
studentName: string;
studentNo: string;
classId: string | number;
className: string;
majorId: string | number;
majorName: string;
collegeId: string | number;
collegeName: string;
campusId: string | number;
campusName: string;
areaId?: string | number;
areaName?: string;
buildingId: string | number;
buildingName: string;
roomId: string | number;
roomName: string;
bedId: string | number;
bedName: string;
holidayType: string;
applyStartDate: string;
applyEndDate: string;
stayReason: string;
stayReasonType: string;
studentPhone: string;
emergencyContact: string;
emergencyPhone: string;
parentConsent: string;
attachmentOssIds?: string;
remark?: string;
createTime: string;
updateTime: string;
}
/**
* 查询宿管员权限内的学生假期留宿信息列表
*/
export function listHolidayStay(query?: HolidayStayQuery): AxiosPromise<any> {
return request({
url: '/studentDynamics/holidayStayList',
method: 'get',
params: query
});
}
/**
* 获取假期留宿详细信息
*/
export function getHolidayStay(id: string | number): AxiosPromise<HolidayStayVO> {
return request({
url: '/studentDynamics/holidayStay/' + id,
method: 'get'
});
}
/**
* 获取假期留宿详细信息(别名)
*/
export const getHolidayStayInfo = getHolidayStay;
// ==================== 学生请假相关接口 ====================
export interface StudentLeaveQuery {
pageNum?: number;
pageSize?: number;
studentName?: string;
studentNo?: string;
collegeName?: string;
majorName?: string;
className?: string;
leaveTypeName?: string;
approvalStatus?: string;
startTime?: string;
endTime?: string;
params?: any;
}
export interface StudentLeaveVO {
id: string | number;
studentId: string | number;
studentName: string;
studentNo: string;
classId: string | number;
className: string;
majorId: string | number;
majorName: string;
collegeId: string | number;
collegeName: string;
leaveTypeCode: string;
leaveTypeName: string;
startTime: string;
endTime: string;
leaveDuration: number;
isLeaveSchool: string;
needCancel: string;
emergencyContact: string;
emergencyPhone: string;
leaveReason: string;
leaveLocation?: string;
attachmentOssIds?: string;
approvalStatus: string;
approvalOpinion?: string;
approvalTime?: string;
approvalBy?: string | number;
approvalByName?: string;
cancelStatus?: string;
cancelTime?: string;
cancelLocation?: string;
cancelPhotoOssIds?: string;
cancelRemark?: string;
createTime: string;
updateTime: string;
}
/**
* 查询宿管员权限内的学生请假信息列表
*/
export function listStudentLeave(query?: StudentLeaveQuery): AxiosPromise<any> {
return request({
url: '/studentDynamics/studentLeaveList',
method: 'get',
params: query
});
}
/**
* 获取学生请假详细信息
*/
export function getStudentLeave(id: string | number): AxiosPromise<StudentLeaveVO> {
return request({
url: '/studentDynamics/studentLeave/' + id,
method: 'get'
});
}
/**
* 获取学生请假详细信息(别名)
*/
export const getStudentLeaveInfo = getStudentLeave;
@@ -0,0 +1,256 @@
<template>
<el-dialog :title="dialogTitle" v-model="dialogOpen" width="800px" append-to-body @close="cancel">
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px" v-loading="loading">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="申请名称:">
<span>{{ form.applicationName }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="申请类型:">
<dict-tag :options="dorm_application_type" :value="form.applicationType" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="学生姓名:">
<span>{{ form.studentName }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="学号:">
<span>{{ form.studentNo }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="学院:">
<span>{{ form.collegeName }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="专业:">
<span>{{ form.majorName }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="班级:">
<span>{{ form.className }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="联系电话:">
<span>{{ form.studentPhone }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="当前宿舍:">
<span>{{ form.currentCampusName }} {{ form.currentBuildingName }} {{ form.currentRoomNo }} {{ form.currentBedNo }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20" v-if="form.applicationType === '1'">
<el-col :span="24">
<el-form-item label="目标宿舍:">
<span>{{ form.targetCampusName }} {{ form.targetBuildingName }} {{ form.targetRoomNo }} {{ form.targetBedNo }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="申请时间:">
<span>{{ form.applyTime }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="审批状态:">
<dict-tag :options="student_leave_approval_status" :value="form.applicationStatus" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="申请原因:" prop="reasonDetail">
<el-input
v-if="isEditable"
v-model="form.reasonDetail"
type="textarea"
:rows="3"
placeholder="请输入申请原因"
maxlength="500"
show-word-limit
/>
<span v-else>{{ form.reasonDetail || '无' }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="备注信息:" prop="remark">
<el-input
v-if="isEditable"
v-model="form.remark"
type="textarea"
:rows="2"
placeholder="请输入备注信息"
maxlength="200"
show-word-limit
/>
<span v-else>{{ form.remark || '无' }}</span>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel">{{ isEditable ? '取消' : '关闭' }}</el-button>
<el-button v-if="isEditable" type="primary" @click="submitForm" :loading="submitLoading">保存</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="DormApplicationDetail">
import { getCurrentInstance, ref, toRefs, computed, type ComponentInternalInstance } from 'vue';
import { ElMessage } from 'element-plus';
import { getDormApplicationInfo, updateDormApplication, type DormApplicationVO } from '@/api/xg/studentDynamics';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { dorm_application_type, student_leave_approval_status } = toRefs(
proxy?.useDict('dorm_application_type', 'student_leave_approval_status') || {}
);
const emit = defineEmits(['refresh']);
// 弹窗状态
const dialogOpen = ref(false);
const loading = ref(false);
const submitLoading = ref(false);
// 表单数据
const form = ref<DormApplicationVO>({} as DormApplicationVO);
// 表单引用
const formRef = ref();
// 表单校验规则
const rules = {
reasonDetail: [{ required: true, message: '请输入申请原因', trigger: 'blur' }]
};
/**
* 是否可编辑
* 草稿、已撤销、已退回状态可编辑
*/
const isEditable = computed(() => {
return ['draft', 'cancel', 'back'].includes(form.value.applicationStatus || '');
});
/**
* 弹窗标题
*/
const dialogTitle = computed(() => {
return isEditable.value ? '修改调宿/退宿申请' : '调宿/退宿申请详情';
});
/**
* 打开弹窗
*/
const openDialog = (id: number) => {
reset();
getDetail(id);
dialogOpen.value = true;
};
/**
* 获取详情数据
*/
const getDetail = async (id: number) => {
loading.value = true;
try {
const response = await getDormApplicationInfo(id);
form.value = response.data;
} catch (error) {
console.error('获取调宿退宿申请详情失败:', error);
ElMessage.error('获取详情失败');
} finally {
loading.value = false;
}
};
/**
* 重置表单
*/
const reset = () => {
form.value = {} as DormApplicationVO;
formRef.value?.resetFields();
};
/**
* 提交表单
*/
const submitForm = async () => {
if (!formRef.value) return;
await formRef.value.validate(async (valid: boolean) => {
if (!valid) return;
submitLoading.value = true;
try {
await updateDormApplication(form.value);
ElMessage.success('保存成功');
dialogOpen.value = false;
emit('refresh');
} catch (error) {
console.error('保存失败:', error);
ElMessage.error('保存失败');
} finally {
submitLoading.value = false;
}
});
};
/**
* 取消操作
*/
const cancel = () => {
dialogOpen.value = false;
reset();
};
// 暴露方法
defineExpose({
open: openDialog
});
</script>
<style scoped>
.dialog-footer {
text-align: right;
}
:deep(.el-form-item__label) {
font-weight: 600;
color: #606266;
}
:deep(.el-form-item__content) {
color: #303133;
}
</style>
@@ -0,0 +1,258 @@
<template>
<div class="page-container">
<!-- 搜索表单 -->
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" style="margin-bottom: 10px">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="学生姓名" prop="studentName">
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="学号" prop="studentNo">
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="学院名称" prop="collegeName">
<el-input v-model="queryParams.collegeName" placeholder="请输入学院名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="专业名称" prop="majorName">
<el-input v-model="queryParams.majorName" placeholder="请输入专业名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="班级名称" prop="className">
<el-input v-model="queryParams.className" placeholder="请输入班级名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="申请类型" prop="applicationType">
<el-select v-model="queryParams.applicationType" placeholder="请选择申请类型" clearable style="width: 100%">
<el-option v-for="dict in dorm_application_type" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="申请时间" prop="applyTime">
<el-date-picker
clearable
v-model="queryParams.applyTime"
type="date"
style="width: 100%"
value-format="YYYY-MM-DD"
placeholder="请选择申请时间"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="审批状态" prop="applicationStatus">
<el-select v-model="queryParams.applicationStatus" placeholder="请选择审批状态" clearable style="width: 100%">
<el-option v-for="dict in student_leave_approval_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
</transition>
<!-- 数据表格 -->
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<span class="card-title">调宿/退宿申请列表</span>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
<el-table-column label="学生姓名" align="center" prop="studentName" />
<el-table-column label="学号" align="center" prop="studentNo" />
<el-table-column label="学院" align="center" prop="collegeName" show-overflow-tooltip />
<el-table-column label="专业" align="center" prop="majorName" show-overflow-tooltip />
<el-table-column label="班级" align="center" prop="className" show-overflow-tooltip />
<el-table-column label="申请类型" align="center">
<template #default="scope">
<dict-tag :options="dorm_application_type" :value="scope.row.applicationType" />
</template>
</el-table-column>
<el-table-column label="当前宿舍" align="center" show-overflow-tooltip>
<template #default="scope"> {{ scope.row.currentBuildingName }}{{ scope.row.currentRoomNo }}{{ scope.row.currentBedNo }} </template>
</el-table-column>
<el-table-column label="目标宿舍" align="center" show-overflow-tooltip>
<template #default="scope">
<span v-if="scope.row.applicationType === '1'">
{{ scope.row.targetBuildingName }}{{ scope.row.targetRoomNo }}{{ scope.row.targetBedNo }}
</span>
<span v-else>—</span>
</template>
</el-table-column>
<el-table-column label="审批状态" align="center">
<template #default="scope">
<dict-tag :options="student_leave_approval_status" :value="scope.row.applicationStatus" />
</template>
</el-table-column>
<el-table-column label="申请时间" align="center" prop="applyTime" width="180" />
<el-table-column label="操作" align="center" width="150" fixed="right">
<template #default="scope">
<el-tooltip content="查看" placement="top">
<el-button
link
type="primary"
icon="View"
@click="handleView(scope.row)"
v-hasPermi="['studentDynamics:dormApplication:query']"
></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
</div>
</template>
<script setup lang="ts" name="DormApplicationTab">
import { getCurrentInstance, ref, onMounted, toRefs, type ComponentInternalInstance } from 'vue';
import { listDormApplication, type DormApplicationQuery } from '@/api/xg/studentDynamics';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const { dorm_application_type, student_leave_approval_status } = toRefs(
proxy?.useDict('dorm_application_type', 'student_leave_approval_status') || {}
);
// 数据状态
const loading = ref(true);
const showSearch = ref(true);
const dataList = ref([]);
const total = ref(0);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
// 查询参数
const queryParams = ref<DormApplicationQuery & any>({
pageNum: 1,
pageSize: 10,
studentName: undefined,
studentNo: undefined,
collegeName: undefined,
majorName: undefined,
className: undefined,
applicationType: undefined,
applicationStatus: undefined,
applyTime: undefined
});
// 表单引用
const queryFormRef = ref();
/**
* 查询列表数据
*/
const getList = async () => {
loading.value = true;
try {
const response = await listDormApplication(queryParams.value);
dataList.value = response.rows;
total.value = response.total;
} catch (error) {
console.error('获取调宿退宿申请列表失败:', error);
proxy?.$modal.msgError('获取数据失败');
} finally {
loading.value = false;
}
};
/**
* 搜索按钮操作
*/
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/**
* 重置按钮操作
*/
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/**
* 多选框选中数据
*/
const handleSelectionChange = (selection: any) => {
ids.value = selection.map((item: any) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
/**
* 查看详情
*/
const handleView = (row: any) => {
// 跳转到调宿退宿申请详情页面
proxy.$router.push({
path: `/xg/xsjcxx/xssssq/dialog`,
query: {
id: row.id,
type: 'update'
}
});
};
// 页面初始化
onMounted(() => {
getList();
});
</script>
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 0;
}
.mb8 {
margin-bottom: 8px;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container .el-form-item {
margin-bottom: 0 !important;
}
.card-title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
</style>
@@ -0,0 +1,474 @@
<template>
<el-dialog title="假期留宿详情" v-model="dialogOpen" width="1000px" append-to-body>
<el-form :model="form" label-width="120px" v-loading="loading">
<el-row :gutter="20">
<!-- 左列:学生基本信息 -->
<el-col :span="12">
<el-form-item label="学生姓名">
<el-input v-model="form.studentName" disabled />
</el-form-item>
<el-form-item label="学号">
<el-input v-model="form.studentNo" disabled />
</el-form-item>
<el-form-item label="学生联系电话">
<el-input v-model="form.studentPhone" disabled />
</el-form-item>
<el-form-item label="学院名称">
<el-input v-model="form.collegeName" disabled />
</el-form-item>
<el-form-item label="专业名称">
<el-input v-model="form.majorName" disabled />
</el-form-item>
<el-form-item label="班级名称">
<el-input v-model="form.className" disabled />
</el-form-item>
</el-col>
<!-- 右列:宿舍信息 -->
<el-col :span="12">
<el-form-item label="校区名称">
<el-input v-model="form.campusName" disabled />
</el-form-item>
<el-form-item label="宿舍区名称">
<el-input v-model="form.areaName" disabled />
</el-form-item>
<el-form-item label="宿舍楼名称">
<el-input v-model="form.buildingName" disabled />
</el-form-item>
<el-form-item label="房间名称">
<el-input v-model="form.roomName" disabled />
</el-form-item>
<el-form-item label="床位名称">
<el-input v-model="form.bedName" disabled />
</el-form-item>
</el-col>
</el-row>
<!-- 假期申请信息 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="假期类型">
<el-input v-model="holidayTypeLabel" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="留宿原因类型">
<el-input v-model="stayReasonTypeLabel" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="留宿开始日期">
<el-input v-model="form.applyStartDate" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="留宿结束日期">
<el-input v-model="form.applyEndDate" disabled />
</el-form-item>
</el-col>
</el-row>
<!-- 联系信息 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="紧急联系人姓名">
<el-input v-model="form.emergencyContact" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="紧急联系人电话">
<el-input v-model="form.emergencyPhone" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="家长同意">
<el-input v-model="parentConsentLabel" disabled />
</el-form-item>
</el-col>
</el-row>
<!-- 留宿原因 -->
<el-form-item label="留宿原因">
<el-input v-model="form.stayReason" type="textarea" :rows="3" disabled />
</el-form-item>
<!-- 附件显示 -->
<el-form-item label="附件">
<div class="attachment-view-container" v-if="attachmentList.length > 0">
<div class="attachment-grid">
<div v-for="file in attachmentList" :key="file.ossId" class="attachment-item">
<!-- 缩略图区域 -->
<div class="attachment-thumb">
<image-preview v-if="isImageFile(file)" :src="file.ossId" class="thumb-image" :isOssId="true" />
<div v-else class="file-placeholder">
<el-icon><Document /></el-icon>
<span class="file-ext">{{ getFileExt(file.name) }}</span>
</div>
</div>
<!-- 文件名称 -->
<div class="attachment-name" :title="file.name">
{{ getShortFileName(file.name) }}
</div>
<!-- 操作按钮 -->
<div class="attachment-actions">
<el-button type="success" size="small" @click="handleDownload(file)" title="下载">
<el-icon><Download /></el-icon>
</el-button>
</div>
</div>
</div>
</div>
<div v-else class="no-attachment">
<el-empty description="暂无附件" :image-size="60" />
</div>
</el-form-item>
<!-- 备注信息 -->
<el-form-item label="备注信息">
<el-input v-model="form.remark" type="textarea" :rows="3" disabled />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogOpen = false">关闭</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="HolidayStayDetail">
import { ref, computed, toRefs, getCurrentInstance, type ComponentInternalInstance } from 'vue';
import ElMessage from 'element-plus/es/components/message/index';
import ElLoading from 'element-plus/es/components/loading/index';
import { Document, Download } from '@element-plus/icons-vue';
import { getHolidayStay, type HolidayStayVO } from '@/api/xg/studentDynamics';
import ImagePreview from '@/components/ImagePreview/index.vue';
import request from '@/utils/request';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
// 字典数据
const { xg_holiday_type, xg_stay_reason_type, sys_yesno } = toRefs<any>(proxy?.useDict('xg_holiday_type', 'xg_stay_reason_type', 'sys_yesno'));
// 对话框状态
const dialogOpen = ref(false);
const loading = ref(false);
// 表单数据
const form = ref<Partial<HolidayStayVO>>({
id: undefined,
studentId: undefined,
studentName: '',
studentNo: '',
classId: undefined,
className: '',
majorId: undefined,
majorName: '',
collegeId: undefined,
collegeName: '',
campusId: undefined,
campusName: '',
areaId: undefined,
areaName: '',
buildingId: undefined,
buildingName: '',
roomId: undefined,
roomName: '',
bedId: undefined,
bedName: '',
holidayType: '',
applyStartDate: '',
applyEndDate: '',
stayReason: '',
stayReasonType: '',
studentPhone: '',
emergencyContact: '',
emergencyPhone: '',
parentConsent: '',
attachmentOssIds: '',
remark: ''
});
// 附件列表
const attachmentList = ref<any[]>([]);
// 字典标签计算属性
const holidayTypeLabel = computed(() => {
if (!form.value.holidayType || !xg_holiday_type.value) return '';
const dict = xg_holiday_type.value.find((item: any) => item.value === form.value.holidayType);
return dict ? dict.label : form.value.holidayType;
});
const stayReasonTypeLabel = computed(() => {
if (!form.value.stayReasonType || !xg_stay_reason_type.value) return '';
const dict = xg_stay_reason_type.value.find((item: any) => item.value === form.value.stayReasonType);
return dict ? dict.label : form.value.stayReasonType;
});
const parentConsentLabel = computed(() => {
if (!form.value.parentConsent || !sys_yesno.value) return '';
const dict = sys_yesno.value.find((item: any) => item.value === form.value.parentConsent);
return dict ? dict.label : form.value.parentConsent;
});
/**
* 打开详情弹窗
*/
const open = async (id: string | number) => {
loading.value = true;
dialogOpen.value = true;
try {
// 获取详情数据
const response = await getHolidayStay(id);
form.value = response.data;
// 处理附件数据
await handleAttachmentData();
} catch (error) {
console.error('获取假期留宿详情失败:', error);
ElMessage.error('获取详情失败');
dialogOpen.value = false;
} finally {
loading.value = false;
}
};
/**
* 处理附件数据
*/
const handleAttachmentData = async () => {
try {
if (form.value.attachmentOssIds && form.value.attachmentOssIds.toString().trim()) {
// 将逗号分隔的字符串转换为数组
const ossIdsArray = form.value.attachmentOssIds
.toString()
.split(',')
.map((id) => id.trim())
.filter((id) => id);
// 调用接口获取附件信息
const response = await request({
url: `/resource/oss/listByIds/${ossIdsArray.join(',')}`,
method: 'get'
});
if (response.code === 200 && response.data) {
// 将附件信息转换为显示需要的格式
attachmentList.value = response.data.map((item: any) => ({
ossId: item.ossId,
name: item.originalName || '附件',
url: item.url,
type: item.contentType || ''
}));
} else {
attachmentList.value = [];
}
} else {
attachmentList.value = [];
}
} catch (error) {
console.error('处理附件数据失败:', error);
attachmentList.value = [];
}
};
/**
* 判断是否为图片文件
*/
const isImageFile = (file: any) => {
const imageTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp'];
return imageTypes.includes(file.type) || /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(file.name);
};
/**
* 获取文件扩展名
*/
const getFileExt = (fileName: string) => {
const ext = fileName.split('.').pop()?.toUpperCase();
return ext || 'FILE';
};
/**
* 获取缩短的文件名
*/
const getShortFileName = (fileName: string) => {
if (!fileName) return '';
const lastDotIndex = fileName.lastIndexOf('.');
if (lastDotIndex === -1) {
return fileName.length > 12 ? fileName.substring(0, 12) + '...' : fileName;
}
const name = fileName.substring(0, lastDotIndex);
const ext = fileName.substring(lastDotIndex);
if (name.length <= 8) {
return fileName;
}
return name.substring(0, 8) + '...' + ext;
};
/**
* 下载附件
*/
const handleDownload = async (file: any) => {
try {
const loading = ElLoading.service({
text: '正在下载文件...',
target: document.body
});
const response = await request({
url: '/resource/oss/download/' + file.ossId,
method: 'get',
responseType: 'blob',
timeout: 300000
});
// 验证响应数据
if (!response) {
throw new Error('响应数据为空');
}
// 检查response的结构
let blobData: Blob;
if (response instanceof Blob) {
blobData = response;
} else if (response.data instanceof Blob) {
blobData = response.data;
} else {
throw new Error('无效的响应数据格式');
}
const url = window.URL.createObjectURL(blobData);
const link = document.createElement('a');
link.href = url;
link.download = file.name;
link.style.display = 'none';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
loading?.close();
ElMessage.success('下载成功');
} catch (error) {
console.error('下载文件失败:', error);
ElMessage.error('下载文件失败,请稍后重试');
}
};
defineExpose({ open });
</script>
<style scoped>
/* 附件查看容器样式 */
.attachment-view-container {
width: 100%;
padding: 12px;
border: 1px solid #e4e7ed;
border-radius: 6px;
background: #fafafa;
}
.attachment-grid {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 12px;
}
.attachment-item {
background: #fff;
border-radius: 6px;
overflow: hidden;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: all 0.3s;
}
.attachment-item:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.attachment-thumb {
position: relative;
width: 100%;
height: 110px;
background: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.thumb-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.file-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #909399;
font-size: 14px;
}
.file-placeholder .el-icon {
font-size: 28px;
margin-bottom: 4px;
}
.file-ext {
font-size: 12px;
text-transform: uppercase;
}
.attachment-name {
padding: 6px 8px;
font-size: 11px;
color: #303133;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
background: #fafafa;
border-top: 1px solid #f0f0f0;
line-height: 1.2;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
.attachment-actions {
display: flex;
justify-content: center;
align-items: center;
padding: 6px 8px;
background: #fafafa;
border-top: 1px solid #f0f0f0;
height: 38px;
}
.attachment-actions .el-button {
padding: 4px 12px;
font-size: 12px;
border-radius: 4px;
}
.no-attachment {
text-align: center;
padding: 20px;
color: #909399;
}
</style>
@@ -0,0 +1,263 @@
<template>
<div class="page-container">
<!-- 搜索表单 -->
<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">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="学生姓名" prop="studentName">
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="学号" prop="studentNo">
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="学院名称" prop="collegeName">
<el-input v-model="queryParams.collegeName" placeholder="请输入学院名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="专业名称" prop="majorName">
<el-input v-model="queryParams.majorName" placeholder="请输入专业名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="班级名称" prop="className">
<el-input v-model="queryParams.className" placeholder="请输入班级名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="假期类型" prop="holidayType">
<el-select v-model="queryParams.holidayType" placeholder="请选择假期类型" clearable style="width: 100%">
<el-option v-for="dict in xg_holiday_type" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="开始日期" prop="applyStartDate">
<el-date-picker
clearable
v-model="queryParams.applyStartDate"
type="date"
style="width: 100%"
value-format="YYYY-MM-DD"
placeholder="请选择开始日期"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="结束日期" prop="applyEndDate">
<el-date-picker
clearable
v-model="queryParams.applyEndDate"
type="date"
style="width: 100%"
value-format="YYYY-MM-DD"
placeholder="请选择结束日期"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
</transition>
<!-- 数据表格 -->
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<span class="card-title">假期留宿信息列表</span>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
<el-table-column label="学生姓名" align="center" prop="studentName" />
<el-table-column label="学号" align="center" prop="studentNo" />
<el-table-column label="学院" align="center" prop="collegeName" show-overflow-tooltip />
<el-table-column label="专业" align="center" prop="majorName" show-overflow-tooltip />
<el-table-column label="班级" align="center" prop="className" show-overflow-tooltip />
<el-table-column label="宿舍信息" align="center" show-overflow-tooltip>
<template #default="scope"> {{ scope.row.buildingName }}{{ scope.row.roomName }} </template>
</el-table-column>
<el-table-column label="假期类型" align="center" prop="holidayType">
<template #default="scope">
<dict-tag :options="xg_holiday_type" :value="scope.row.holidayType" />
</template>
</el-table-column>
<el-table-column label="留宿时间" align="center" show-overflow-tooltip>
<template #default="scope"> {{ scope.row.applyStartDate }} 至 {{ scope.row.applyEndDate }} </template>
</el-table-column>
<el-table-column label="留宿原因类型" align="center" prop="stayReasonType">
<template #default="scope">
<dict-tag :options="xg_stay_reason_type" :value="scope.row.stayReasonType" />
</template>
</el-table-column>
<el-table-column label="联系电话" align="center" prop="studentPhone" />
<el-table-column label="紧急联系人" align="center" prop="emergencyContact" />
<el-table-column label="操作" align="center" width="150" fixed="right">
<template #default="scope">
<el-tooltip content="查看" placement="top">
<el-button
link
type="primary"
icon="View"
@click="handleView(scope.row)"
v-hasPermi="['studentDynamics:holidayStay:query']"
></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 查看详情弹窗 -->
<HolidayStayDetail ref="detailRef" @refresh="getList" />
</div>
</template>
<script setup lang="ts" name="HolidayStayTab">
import { getCurrentInstance, ref, onMounted, toRefs } from 'vue';
import { ElMessage } from 'element-plus';
import { listHolidayStay, type HolidayStayQuery } from '@/api/xg/studentDynamics';
import HolidayStayDetail from './HolidayStayDetail.vue';
const { proxy } = getCurrentInstance();
// 字典数据
const { xg_holiday_type, xg_stay_reason_type } = toRefs(proxy?.useDict('xg_holiday_type', 'xg_stay_reason_type') || {});
// 数据状态
const loading = ref(true);
const showSearch = ref(true);
const dataList = ref([]);
const total = ref(0);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
// 查询参数
const queryParams = ref<HolidayStayQuery & any>({
pageNum: 1,
pageSize: 10,
studentName: undefined,
studentNo: undefined,
collegeName: undefined,
majorName: undefined,
className: undefined,
holidayType: undefined,
applyStartDate: undefined,
applyEndDate: undefined,
status: undefined
});
// 表单引用
const queryFormRef = ref();
const detailRef = ref();
/**
* 查询列表数据
*/
const getList = async () => {
loading.value = true;
try {
const response = await listHolidayStay(queryParams.value);
dataList.value = response.rows;
total.value = response.total;
} catch (error) {
console.error('获取假期留宿信息列表失败:', error);
ElMessage.error('获取数据失败');
} finally {
loading.value = false;
}
};
/**
* 搜索按钮操作
*/
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/**
* 重置按钮操作
*/
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/**
* 多选框选中数据
*/
const handleSelectionChange = (selection: any) => {
ids.value = selection.map((item: any) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
/**
* 查看详情
*/
const handleView = (row: any) => {
detailRef.value?.open(row.id);
};
// 页面初始化
onMounted(() => {
getList();
});
</script>
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 0;
}
.mb-[10px] {
margin-bottom: 10px;
}
.mb8 {
margin-bottom: 8px;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container .el-form-item {
margin-bottom: 0 !important;
}
.card-title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
</style>
@@ -0,0 +1,21 @@
<template>
<el-dialog title="学生请假详情" v-model="dialogOpen" width="800px" append-to-body>
<div>学生请假详情组件待实现</div>
<template #footer>
<el-button @click="dialogOpen = false">关闭</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="StudentLeaveDetail">
import { ref } from 'vue';
const dialogOpen = ref(false);
const open = (id: number) => {
console.log('打开学生请假详情,ID:', id);
dialogOpen.value = true;
};
defineExpose({ open });
</script>
@@ -0,0 +1,271 @@
<template>
<div class="page-container">
<!-- 搜索表单 -->
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" style="margin-bottom: 10px">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" label-width="100px">
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="学生姓名" prop="studentName">
<el-input v-model="queryParams.studentName" placeholder="请输入学生姓名" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="学号" prop="studentNo">
<el-input v-model="queryParams.studentNo" placeholder="请输入学号" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="学院名称" prop="collegeName">
<el-input v-model="queryParams.collegeName" placeholder="请输入学院名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="专业名称" prop="majorName">
<el-input v-model="queryParams.majorName" placeholder="请输入专业名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="班级名称" prop="className">
<el-input v-model="queryParams.className" placeholder="请输入班级名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="请假类型" prop="leaveTypeName">
<el-input v-model="queryParams.leaveTypeName" placeholder="请输入请假类型名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="开始时间" prop="startTime">
<el-date-picker
clearable
v-model="queryParams.startTime"
type="date"
style="width: 100%"
value-format="YYYY-MM-DD"
placeholder="请选择开始时间"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="结束时间" prop="endTime">
<el-date-picker
clearable
v-model="queryParams.endTime"
type="date"
style="width: 100%"
value-format="YYYY-MM-DD"
placeholder="请选择结束时间"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="审批状态" prop="approvalStatus">
<el-select v-model="queryParams.approvalStatus" placeholder="请选择审批状态" clearable style="width: 100%">
<el-option v-for="dict in student_leave_approval_status" :key="dict.value" :label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="24" class="search-btn-container">
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
</div>
</transition>
<!-- 数据表格 -->
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<span class="card-title">学生请假信息列表</span>
</el-col>
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
</template>
<el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
<el-table-column label="学生姓名" align="center" prop="studentName" />
<el-table-column label="学号" align="center" prop="studentNo" />
<el-table-column label="学院" align="center" prop="collegeName" show-overflow-tooltip />
<el-table-column label="专业" align="center" prop="majorName" show-overflow-tooltip />
<el-table-column label="班级" align="center" prop="className" show-overflow-tooltip />
<el-table-column label="请假类型" align="center" prop="leaveTypeName" />
<el-table-column label="请假时间" align="center" show-overflow-tooltip>
<template #default="scope"> {{ scope.row.startTime }} 至 {{ scope.row.endTime }} </template>
</el-table-column>
<el-table-column label="请假时长" align="center">
<template #default="scope"> {{ scope.row.leaveDuration }}天 </template>
</el-table-column>
<el-table-column label="是否离校" align="center">
<template #default="scope">
<dict-tag :options="sys_yesno" :value="scope.row.isLeaveSchool" />
</template>
</el-table-column>
<el-table-column label="审批状态" align="center">
<template #default="scope">
<dict-tag :options="student_leave_approval_status" :value="scope.row.approvalStatus" />
</template>
</el-table-column>
<el-table-column label="销假状态" align="center">
<template #default="scope">
<dict-tag :options="xg_xsqj_cancel_status" :value="scope.row.cancelStatus" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="150" fixed="right">
<template #default="scope">
<el-tooltip content="查看" placement="top">
<el-button
link
type="primary"
icon="View"
@click="handleView(scope.row)"
v-hasPermi="['studentDynamics:studentLeave:query']"
></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
</div>
</template>
<script setup lang="ts" name="StudentLeaveTab">
import { getCurrentInstance, ref, onMounted, toRefs } from 'vue';
import { listStudentLeave, type StudentLeaveQuery } from '@/api/xg/studentDynamics';
const { proxy } = getCurrentInstance();
const { sys_yesno, student_leave_approval_status, xg_xsqj_cancel_status } = toRefs(
proxy?.useDict('sys_yesno', 'student_leave_approval_status', 'xg_xsqj_cancel_status') || {}
);
// 数据状态
const loading = ref(true);
const showSearch = ref(true);
const dataList = ref([]);
const total = ref(0);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
// 查询参数
const queryParams = ref<StudentLeaveQuery & any>({
pageNum: 1,
pageSize: 10,
studentName: undefined,
studentNo: undefined,
collegeName: undefined,
majorName: undefined,
className: undefined,
leaveTypeName: undefined,
approvalStatus: undefined,
startTime: undefined,
endTime: undefined
});
// 表单引用
const queryFormRef = ref();
/**
* 查询列表数据
*/
const getList = async () => {
loading.value = true;
try {
const response = await listStudentLeave(queryParams.value);
dataList.value = response.rows;
total.value = response.total;
} catch (error) {
console.error('获取学生请假信息列表失败:', error);
proxy?.$modal.msgError('获取数据失败');
} finally {
loading.value = false;
}
};
/**
* 搜索按钮操作
*/
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
};
/**
* 重置按钮操作
*/
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
};
/**
* 多选框选中数据
*/
const handleSelectionChange = (selection: any) => {
ids.value = selection.map((item: any) => item.id);
single.value = selection.length !== 1;
multiple.value = !selection.length;
};
/**
* 查看详情
*/
const handleView = (row: any) => {
// 跳转到学生请假详情页面
proxy.$router.push({
path: `/xg/xsjcxx/xsqj/leaveEdit/index`,
query: {
id: row.id,
type: 'update'
}
});
};
// 页面初始化
onMounted(() => {
getList();
});
</script>
<style scoped>
/* 页面容器样式 */
.page-container {
padding: 0;
}
.mb8 {
margin-bottom: 8px;
}
/* 搜索按钮容器样式 */
.search-btn-container {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}
.search-btn-container .el-form-item {
margin-bottom: 0 !important;
}
.card-title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
</style>
@@ -0,0 +1,80 @@
<template>
<div class="p-2">
<!-- Tab页组件 -->
<el-tabs v-model="activeTab" @tab-click="handleTabClick" class="mb-4">
<!-- 调宿/退宿信息Tab页 -->
<el-tab-pane label="调宿/退宿信息" name="dormApplication">
<DormApplicationTab v-if="activeTab === 'dormApplication'" ref="dormApplicationTabRef" />
</el-tab-pane>
<!-- 假期留宿信息Tab页 -->
<el-tab-pane label="假期留宿信息" name="holidayStay">
<HolidayStayTab v-if="activeTab === 'holidayStay'" ref="holidayStayTabRef" />
</el-tab-pane>
<!-- 请假信息Tab页 -->
<el-tab-pane label="请假信息" name="studentLeave">
<StudentLeaveTab v-if="activeTab === 'studentLeave'" ref="studentLeaveTabRef" />
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup lang="ts" name="StudentDynamics">
import { ref, onMounted } from 'vue';
import DormApplicationTab from './components/DormApplicationTab.vue';
import HolidayStayTab from './components/HolidayStayTab.vue';
import StudentLeaveTab from './components/StudentLeaveTab.vue';
// Tab页状态
const activeTab = ref('dormApplication');
// 子组件引用
const dormApplicationTabRef = ref();
const holidayStayTabRef = ref();
const studentLeaveTabRef = ref();
/**
* Tab页切换处理
*/
const handleTabClick = (tab: any) => {
console.log('切换到Tab页:', tab.name);
// 可以在这里添加Tab切换的逻辑
};
/**
* 页面初始化
*/
onMounted(() => {
console.log('学生动向信息管理页面已加载');
});
</script>
<style scoped>
.p-2 {
padding: 8px;
}
.mb-4 {
margin-bottom: 16px;
}
/* Tab页样式优化 */
:deep(.el-tabs__header) {
margin-bottom: 20px;
}
:deep(.el-tabs__item) {
font-size: 14px;
font-weight: 500;
}
:deep(.el-tabs__item.is-active) {
/* color: #409eff; */
font-weight: 600;
}
:deep(.el-tabs__content) {
padding: 0;
}
</style>