提交代码
This commit is contained in:
1 parent
eb51f1645c
commit
c164d1feb4
21 files changed
+1721
-381
No files matched your search
@@ -313,3 +313,41 @@ system:
|
||||
# - 对于频繁访问的小文件,可以增加 max-cache-size
|
||||
# - 对于大文件较多的环境,可以调整 max-single-file-size
|
||||
# - 对于访问频率较低的环境,可以适当减少 expire-time-ms
|
||||
|
||||
|
||||
--- # SQL日志配置(用于调试班级名额查询问题)
|
||||
logging:
|
||||
level:
|
||||
# 开启 MyBatis SQL 日志
|
||||
org.dromara.jcbt.mapper: DEBUG
|
||||
org.dromara.jcbt.service: DEBUG
|
||||
# 开启 MyBatis-Plus SQL 日志
|
||||
com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor: DEBUG
|
||||
# 开启 HikariCP 连接池日志(可选)
|
||||
com.zaxxer.hikari: DEBUG
|
||||
# 开启 P6Spy SQL 日志(更详细的SQL执行日志)
|
||||
p6spy: INFO
|
||||
pattern:
|
||||
# 控制台日志格式,增加线程名和方法名
|
||||
console: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx"
|
||||
|
||||
# MyBatis 配置
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
# 开启 SQL 日志打印
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
# 开启驼峰命名转换
|
||||
map-underscore-to-camel-case: true
|
||||
# 开启缓存
|
||||
cache-enabled: true
|
||||
# 开启懒加载
|
||||
lazy-loading-enabled: true
|
||||
# 开启积极懒加载
|
||||
aggressive-lazy-loading: false
|
||||
|
||||
--- # 高德地图配置
|
||||
amap:
|
||||
# 高德地图API密钥(请替换为您的实际密钥)
|
||||
key: 920750e421cd6357ddf60ee1e929e0dd
|
||||
# 高德地图API基础URL
|
||||
url: https://restapi.amap.com
|
||||
+15
@@ -149,4 +149,19 @@ public class XgDormHygieneCheckController extends BaseController {
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据学生ID分页查询该学生宿舍的卫生检查信息
|
||||
*
|
||||
* @param studentId 学生ID
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
*/
|
||||
@SaCheckPermission("dormHygieneCheck:dormHygieneCheck:query")
|
||||
@GetMapping("/student/{studentId}/page")
|
||||
public TableDataInfo<XgDormHygieneCheckVo> getHygieneChecksByStudentIdPage(
|
||||
@NotNull(message = "学生ID不能为空") @PathVariable Long studentId,
|
||||
XgDormHygieneCheckBo bo, PageQuery pageQuery) {
|
||||
return xgDormHygieneCheckService.queryPageListByStudentId(studentId, bo, pageQuery);
|
||||
}
|
||||
|
||||
}
|
||||
+11
@@ -100,4 +100,15 @@ public interface IXgDormHygieneCheckService {
|
||||
*/
|
||||
List<XgDormHygieneCheckVo> queryByStudentId(Long studentId);
|
||||
|
||||
/**
|
||||
* 根据学生ID分页查询该学生宿舍的卫生检查信息
|
||||
*
|
||||
* @param studentId 学生ID
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 卫生检查信息分页列表
|
||||
*/
|
||||
TableDataInfo<XgDormHygieneCheckVo> queryPageListByStudentId(Long studentId, XgDormHygieneCheckBo bo,
|
||||
PageQuery pageQuery);
|
||||
|
||||
}
|
||||
+160
-29
@@ -610,10 +610,10 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
}
|
||||
|
||||
// 3. 检查床位是否已分配给学院(必须先分配给学院才能分配给班级)
|
||||
// 修复:查询所有已分配给学院的床位,不论是否已分配班级
|
||||
LambdaQueryWrapper<XgDormBedStudent> collegeCheckWrapper = new LambdaQueryWrapper<>();
|
||||
collegeCheckWrapper.in(XgDormBedStudent::getBedId, bo.getBedIds())
|
||||
.isNotNull(XgDormBedStudent::getCollegeId)
|
||||
.isNull(XgDormBedStudent::getClassId) // 只分配了学院,还没分配班级
|
||||
.isNotNull(XgDormBedStudent::getCollegeId) // 已分配给学院
|
||||
.eq(XgDormBedStudent::getStatus, "0"); // 正常状态,未退宿
|
||||
|
||||
List<XgDormBedStudent> existingCollegeAllocations = baseMapper.selectList(collegeCheckWrapper);
|
||||
@@ -640,6 +640,9 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
throw new RuntimeException("以下床位尚未分配给学院,无法分配给班级:" + String.join("、", unallocatedBedNames));
|
||||
}
|
||||
|
||||
log.info("床位预检验证通过:75个床位中,{}个已分配给学院,{}个未分配给学院",
|
||||
collegeAllocatedBedIds.size(), unallocatedBedIds.size());
|
||||
|
||||
// 5. 获取楼栋信息,用于获取校区ID和宿舍区ID
|
||||
XgDormBuilding building = xgDormBuildingMapper.selectById(bo.getBuildingId());
|
||||
if (building == null) {
|
||||
@@ -725,6 +728,9 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
int[] roomUsage = new int[sortedRoomIds.size()]; // 记录每个房间已使用床位数
|
||||
Map<Integer, Long> roomClassMap = new java.util.HashMap<>(); // 记录房间主要分配给哪个班级
|
||||
|
||||
// 新增:详细的床位分配记录,记录每个床位分配给哪个班级
|
||||
Map<Long, Long> bedClassMap = new java.util.HashMap<>(); // bedId -> classId 的映射
|
||||
|
||||
// 按班级顺序,采用房间优先分配策略
|
||||
for (int classIndex = 0; classIndex < bo.getClassIds().size(); classIndex++) {
|
||||
Long classId = bo.getClassIds().get(classIndex);
|
||||
@@ -744,6 +750,12 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
// 分配整个房间给当前班级
|
||||
roomUsage[roomIndex] = roomCapacity;
|
||||
roomClassMap.put(roomIndex, classId);
|
||||
|
||||
// 记录每个床位的分配
|
||||
for (XgDormBed bed : roomBeds) {
|
||||
bedClassMap.put(bed.getId(), classId);
|
||||
}
|
||||
|
||||
remainingStudents -= roomCapacity;
|
||||
|
||||
log.info("为班级{}分配完整房间{},房间编码{},床位数{}",
|
||||
@@ -760,6 +772,16 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
// 优先分配到已有该班级学生的房间
|
||||
if (availableBedsCount > 0 && classId.equals(roomClassMap.get(roomIndex))) {
|
||||
int bedsToAllocate = Math.min(remainingStudents, availableBedsCount);
|
||||
|
||||
// 从房间的未分配床位中选择床位分配给当前班级
|
||||
int allocated = 0;
|
||||
for (XgDormBed bed : roomBeds) {
|
||||
if (!bedClassMap.containsKey(bed.getId()) && allocated < bedsToAllocate) {
|
||||
bedClassMap.put(bed.getId(), classId);
|
||||
allocated++;
|
||||
}
|
||||
}
|
||||
|
||||
roomUsage[roomIndex] += bedsToAllocate;
|
||||
remainingStudents -= bedsToAllocate;
|
||||
|
||||
@@ -777,6 +799,16 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
// 分配到完全空闲的房间
|
||||
if (availableBedsCount > 0 && roomClassMap.get(roomIndex) == null) {
|
||||
int bedsToAllocate = Math.min(remainingStudents, availableBedsCount);
|
||||
|
||||
// 从房间的未分配床位中选择床位分配给当前班级
|
||||
int allocated = 0;
|
||||
for (XgDormBed bed : roomBeds) {
|
||||
if (!bedClassMap.containsKey(bed.getId()) && allocated < bedsToAllocate) {
|
||||
bedClassMap.put(bed.getId(), classId);
|
||||
allocated++;
|
||||
}
|
||||
}
|
||||
|
||||
roomUsage[roomIndex] += bedsToAllocate;
|
||||
roomClassMap.put(roomIndex, classId);
|
||||
remainingStudents -= bedsToAllocate;
|
||||
@@ -795,6 +827,16 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
// 分配到任何有空闲床位的房间,即使会产生混住
|
||||
if (availableBedsCount > 0) {
|
||||
int bedsToAllocate = Math.min(remainingStudents, availableBedsCount);
|
||||
|
||||
// 从房间的未分配床位中选择床位分配给当前班级
|
||||
int allocated = 0;
|
||||
for (XgDormBed bed : roomBeds) {
|
||||
if (!bedClassMap.containsKey(bed.getId()) && allocated < bedsToAllocate) {
|
||||
bedClassMap.put(bed.getId(), classId);
|
||||
allocated++;
|
||||
}
|
||||
}
|
||||
|
||||
roomUsage[roomIndex] += bedsToAllocate;
|
||||
remainingStudents -= bedsToAllocate;
|
||||
|
||||
@@ -820,38 +862,38 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
}
|
||||
|
||||
// 12. 批量更新床位学生记录,补充班级和专业信息
|
||||
// 使用新的bedClassMap确保准确分配
|
||||
int totalUpdatedRecords = 0;
|
||||
|
||||
for (int i = 0; i < bo.getClassIds().size(); i++) {
|
||||
Long classId = bo.getClassIds().get(i);
|
||||
XgClassMsg classInfo = classMap.get(classId);
|
||||
|
||||
// 计算应该分配给该班级的床位数量
|
||||
int classStudentCount = classCounts.get(i);
|
||||
// 从bedClassMap中获取分配给该班级的床位ID列表
|
||||
List<Long> classBedIds = bedClassMap.entrySet().stream()
|
||||
.filter(entry -> classId.equals(entry.getValue()))
|
||||
.map(Map.Entry::getKey)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
// 获取该班级应该分配的床位ID列表(按分配算法确定的顺序)
|
||||
List<Long> classBedIds = new ArrayList<>();
|
||||
int allocatedCount = 0;
|
||||
|
||||
// 按房间分配顺序找出分配给该班级的床位
|
||||
for (int roomIndex = 0; roomIndex < sortedRoomIds.size()
|
||||
&& allocatedCount < classStudentCount; roomIndex++) {
|
||||
if (classId.equals(roomClassMap.get(roomIndex))) {
|
||||
Long roomId = sortedRoomIds.get(roomIndex);
|
||||
List<XgDormBed> roomBeds = bedsByRoom.get(roomId);
|
||||
|
||||
// 计算这个房间分配给该班级的床位数
|
||||
int roomBedsForThisClass = Math.min(classStudentCount - allocatedCount, roomBeds.size());
|
||||
|
||||
for (int j = 0; j < roomBedsForThisClass; j++) {
|
||||
classBedIds.add(roomBeds.get(j).getId());
|
||||
allocatedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("班级{}({})应分配床位数:{},实际分配床位ID:{}",
|
||||
classInfo.getClassName(), classId, classCounts.get(i), classBedIds);
|
||||
|
||||
if (!classBedIds.isEmpty()) {
|
||||
// 批量更新该班级分配的床位记录
|
||||
LambdaUpdateWrapper<XgDormBedStudent> updateWrapper = Wrappers.lambdaUpdate();
|
||||
updateWrapper.in(XgDormBedStudent::getBedId, classBedIds)
|
||||
// 先查询现有记录状态,便于诊断问题
|
||||
LambdaQueryWrapper<XgDormBedStudent> queryWrapper = Wrappers.lambdaQuery();
|
||||
queryWrapper.in(XgDormBedStudent::getBedId, classBedIds)
|
||||
.eq(XgDormBedStudent::getStatus, "0");
|
||||
List<XgDormBedStudent> existingRecords = baseMapper.selectList(queryWrapper);
|
||||
|
||||
log.info("床位记录查询结果:共{}条记录,其中已分配班级的{}条,未分配班级的{}条",
|
||||
existingRecords.size(),
|
||||
existingRecords.stream().mapToInt(r -> r.getClassId() != null ? 1 : 0).sum(),
|
||||
existingRecords.stream().mapToInt(r -> r.getClassId() == null ? 1 : 0).sum());
|
||||
|
||||
// 分两种情况处理:新分配和重新分配
|
||||
// 情况1:更新未分配班级的记录
|
||||
LambdaUpdateWrapper<XgDormBedStudent> updateWrapper1 = Wrappers.lambdaUpdate();
|
||||
updateWrapper1.in(XgDormBedStudent::getBedId, classBedIds)
|
||||
.eq(XgDormBedStudent::getStatus, "0") // 正常状态
|
||||
.isNull(XgDormBedStudent::getClassId) // 只更新还没分配班级的记录
|
||||
.isNotNull(XgDormBedStudent::getCollegeId) // 已分配给学院
|
||||
@@ -860,11 +902,100 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService {
|
||||
.set(XgDormBedStudent::getUpdateTime, now)
|
||||
.set(XgDormBedStudent::getUpdateBy, loginUserId);
|
||||
|
||||
int updateCount = baseMapper.update(null, updateWrapper);
|
||||
log.info("为班级{}({})更新了{}条床位分配记录", classInfo.getClassName(), classId, updateCount);
|
||||
int updateCount1 = baseMapper.update(null, updateWrapper1);
|
||||
|
||||
// 情况2:强制更新所有指定床位的班级分配(覆盖之前的分配)
|
||||
LambdaUpdateWrapper<XgDormBedStudent> updateWrapper2 = Wrappers.lambdaUpdate();
|
||||
updateWrapper2.in(XgDormBedStudent::getBedId, classBedIds)
|
||||
.eq(XgDormBedStudent::getStatus, "0") // 正常状态
|
||||
.isNotNull(XgDormBedStudent::getCollegeId) // 已分配给学院
|
||||
.isNotNull(XgDormBedStudent::getClassId) // 已分配班级的记录
|
||||
.set(XgDormBedStudent::getMajorId, classInfo.getMajorId())
|
||||
.set(XgDormBedStudent::getClassId, classInfo.getId())
|
||||
.set(XgDormBedStudent::getUpdateTime, now)
|
||||
.set(XgDormBedStudent::getUpdateBy, loginUserId);
|
||||
|
||||
int updateCount2 = baseMapper.update(null, updateWrapper2);
|
||||
|
||||
int totalUpdateCount = updateCount1 + updateCount2;
|
||||
totalUpdatedRecords += totalUpdateCount;
|
||||
|
||||
log.info("为班级{}({})更新了{}条床位分配记录(新分配{}条,重新分配{}条),预期{}条",
|
||||
classInfo.getClassName(), classId, totalUpdateCount, updateCount1, updateCount2,
|
||||
classBedIds.size());
|
||||
|
||||
// 验证更新结果
|
||||
if (totalUpdateCount != classBedIds.size()) {
|
||||
log.error("班级{}床位分配不完整:预期更新{}条记录,实际更新{}条记录",
|
||||
classInfo.getClassName(), classBedIds.size(), totalUpdateCount);
|
||||
|
||||
// 详细诊断:查询每个床位的具体状态
|
||||
for (Long bedId : classBedIds) {
|
||||
LambdaQueryWrapper<XgDormBedStudent> bedQuery = Wrappers.lambdaQuery();
|
||||
bedQuery.eq(XgDormBedStudent::getBedId, bedId);
|
||||
List<XgDormBedStudent> bedRecords = baseMapper.selectList(bedQuery);
|
||||
log.error("床位{}的记录状态:{}", bedId,
|
||||
bedRecords.isEmpty() ? "无记录"
|
||||
: bedRecords.stream()
|
||||
.map(r -> String.format("status=%s,collegeId=%s,classId=%s",
|
||||
r.getStatus(), r.getCollegeId(), r.getClassId()))
|
||||
.collect(java.util.stream.Collectors.joining(",")));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.warn("班级{}({})没有分配到任何床位", classInfo.getClassName(), classId);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("总计更新了{}条床位分配记录", totalUpdatedRecords);
|
||||
|
||||
// 验证总体分配结果
|
||||
int totalAllocatedBeds = bedClassMap.size();
|
||||
int totalExpectedBeds = classCounts.stream().mapToInt(Integer::intValue).sum();
|
||||
log.info("床位分配验证:总可用床位{},预期分配{},实际分配{},剩余床位{}",
|
||||
totalAvailableBeds, totalExpectedBeds, totalAllocatedBeds,
|
||||
totalAvailableBeds - totalAllocatedBeds);
|
||||
|
||||
// 当床位数量完全匹配需求时,必须全部分配完
|
||||
if (totalExpectedBeds == totalAvailableBeds && totalAllocatedBeds < totalExpectedBeds) {
|
||||
log.error("严重错误:床位数量完全匹配需求({}个),但实际只分配了{}个床位,剩余{}个床位未分配",
|
||||
totalExpectedBeds, totalAllocatedBeds, totalExpectedBeds - totalAllocatedBeds);
|
||||
|
||||
// 详细诊断未分配的床位
|
||||
Set<Long> allocatedBedIds = bedClassMap.keySet();
|
||||
List<Long> unallocatedBeds = collegeAllocatedBedIds.stream()
|
||||
.filter(bedId -> !allocatedBedIds.contains(bedId))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
|
||||
log.error("未分配的床位ID:{}", unallocatedBeds);
|
||||
|
||||
// 分析未分配床位的房间分布
|
||||
Map<Long, List<Long>> unallocatedByRoom = unallocatedBeds.stream()
|
||||
.collect(java.util.stream.Collectors.groupingBy(bedId -> {
|
||||
return beds.stream()
|
||||
.filter(bed -> bed.getId().equals(bedId))
|
||||
.findFirst()
|
||||
.map(XgDormBed::getRoomId)
|
||||
.orElse(0L);
|
||||
}));
|
||||
|
||||
for (Map.Entry<Long, List<Long>> entry : unallocatedByRoom.entrySet()) {
|
||||
Long roomId = entry.getKey();
|
||||
List<Long> roomUnallocatedBeds = entry.getValue();
|
||||
String roomCode = roomCodeMap.get(roomId);
|
||||
log.error("房间{}({})有{}个床位未分配:{}", roomCode, roomId, roomUnallocatedBeds.size(), roomUnallocatedBeds);
|
||||
}
|
||||
|
||||
throw new RuntimeException("床位分配不完整,存在" + (totalExpectedBeds - totalAllocatedBeds) + "个床位未分配");
|
||||
}
|
||||
|
||||
// 一般情况下的验证
|
||||
if (totalExpectedBeds <= totalAvailableBeds && totalAllocatedBeds < totalExpectedBeds) {
|
||||
log.error("床位分配不完整:应分配{}个床位,实际只分配了{}个床位",
|
||||
totalExpectedBeds, totalAllocatedBeds);
|
||||
throw new RuntimeException("床位分配不完整,存在未分配的床位");
|
||||
}
|
||||
|
||||
log.info("成功更新{}个床位分配给{}个班级,总可用床位:{}",
|
||||
bo.getBedIds().size(), bo.getClassIds().size(), totalAvailableBeds);
|
||||
return true;
|
||||
|
||||
+66
@@ -423,6 +423,72 @@ public class XgDormHygieneCheckServiceImpl implements IXgDormHygieneCheckService
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据学生ID分页查询该学生宿舍的卫生检查信息
|
||||
*
|
||||
* @param studentId 学生ID
|
||||
* @param bo 查询条件
|
||||
* @param pageQuery 分页参数
|
||||
* @return 卫生检查信息分页列表
|
||||
*/
|
||||
@Override
|
||||
public TableDataInfo<XgDormHygieneCheckVo> queryPageListByStudentId(Long studentId, XgDormHygieneCheckBo bo,
|
||||
PageQuery pageQuery) {
|
||||
if (studentId == null) {
|
||||
log.warn("学生ID为空,无法查询卫生检查信息");
|
||||
return TableDataInfo.build();
|
||||
}
|
||||
|
||||
// 1. 通过学生ID查询学生评分记录,获取卫生检查ID列表
|
||||
LambdaQueryWrapper<XgDormHygieneStudentScore> studentScoreWrapper = Wrappers.lambdaQuery();
|
||||
studentScoreWrapper.eq(XgDormHygieneStudentScore::getStudentId, studentId);
|
||||
studentScoreWrapper.eq(XgDormHygieneStudentScore::getDeleted, 0); // 只查询未删除的记录
|
||||
List<XgDormHygieneStudentScore> studentScores = studentScoreMapper.selectList(studentScoreWrapper);
|
||||
|
||||
if (studentScores.isEmpty()) {
|
||||
log.info("学生ID: {} 没有找到相关的卫生检查记录", studentId);
|
||||
return TableDataInfo.build();
|
||||
}
|
||||
|
||||
// 2. 提取卫生检查ID列表
|
||||
List<Long> hygieneCheckIds = studentScores.stream()
|
||||
.map(XgDormHygieneStudentScore::getHygieneCheckId)
|
||||
.distinct()
|
||||
.toList();
|
||||
|
||||
// 3. 构建查询条件,结合传入的bo参数和学生相关的检查ID
|
||||
LambdaQueryWrapper<XgDormHygieneCheck> lqw = Wrappers.lambdaQuery();
|
||||
|
||||
// 基础条件:只查询该学生相关的检查记录
|
||||
lqw.in(XgDormHygieneCheck::getId, hygieneCheckIds);
|
||||
lqw.eq(XgDormHygieneCheck::getDeleted, 0); // 只查询未删除的记录
|
||||
|
||||
// 应用搜索条件(如果有)
|
||||
if (bo != null) {
|
||||
lqw.like(StringUtils.isNotBlank(bo.getCheckTitle()), XgDormHygieneCheck::getCheckTitle, bo.getCheckTitle());
|
||||
lqw.eq(bo.getCheckDate() != null, XgDormHygieneCheck::getCheckDate, bo.getCheckDate());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getCheckerName()), XgDormHygieneCheck::getCheckerName,
|
||||
bo.getCheckerName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getCheckType()), XgDormHygieneCheck::getCheckType, bo.getCheckType());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getCheckLevel()), XgDormHygieneCheck::getCheckLevel, bo.getCheckLevel());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getHygieneGrade()), XgDormHygieneCheck::getHygieneGrade,
|
||||
bo.getHygieneGrade());
|
||||
lqw.like(StringUtils.isNotBlank(bo.getCheckSummary()), XgDormHygieneCheck::getCheckSummary,
|
||||
bo.getCheckSummary());
|
||||
}
|
||||
|
||||
// 按创建时间倒序排列
|
||||
lqw.orderByDesc(XgDormHygieneCheck::getCreateTime);
|
||||
|
||||
// 4. 分页查询
|
||||
Page<XgDormHygieneCheckVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||
|
||||
log.info("学生ID: {} 分页查询卫生检查记录 - 页码: {}, 页大小: {}, 总数: {}",
|
||||
studentId, pageQuery.getPageNum(), pageQuery.getPageSize(), result.getTotal());
|
||||
|
||||
return TableDataInfo.build(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换检查类型代码为文本
|
||||
*/
|
||||
|
||||
+8
-7
@@ -40,7 +40,7 @@ public class XgStuColBatchServiceImpl implements IXgStuColBatchService {
|
||||
* @return 学生信息收集批次
|
||||
*/
|
||||
@Override
|
||||
public XgStuColBatchVo queryById(Long id){
|
||||
public XgStuColBatchVo queryById(Long id) {
|
||||
return baseMapper.selectVoById(id);
|
||||
}
|
||||
|
||||
@@ -73,10 +73,11 @@ public class XgStuColBatchServiceImpl implements IXgStuColBatchService {
|
||||
private LambdaQueryWrapper<XgStuColBatch> buildQueryWrapper(XgStuColBatchBo bo) {
|
||||
Map<String, Object> params = bo.getParams();
|
||||
LambdaQueryWrapper<XgStuColBatch> lqw = Wrappers.lambdaQuery();
|
||||
lqw.orderByAsc(XgStuColBatch::getId);
|
||||
lqw.orderByDesc(XgStuColBatch::getCreateTime);
|
||||
lqw.like(StringUtils.isNotBlank(bo.getBatchName()), XgStuColBatch::getBatchName, bo.getBatchName());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getStatus()), XgStuColBatch::getStatus, bo.getStatus());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getCollectionType()), XgStuColBatch::getCollectionType, bo.getCollectionType());
|
||||
lqw.eq(StringUtils.isNotBlank(bo.getCollectionType()), XgStuColBatch::getCollectionType,
|
||||
bo.getCollectionType());
|
||||
return lqw;
|
||||
}
|
||||
|
||||
@@ -113,8 +114,8 @@ public class XgStuColBatchServiceImpl implements IXgStuColBatchService {
|
||||
/**
|
||||
* 保存前的数据校验
|
||||
*/
|
||||
private void validEntityBeforeSave(XgStuColBatch entity){
|
||||
//TODO 做一些数据校验,如唯一约束
|
||||
private void validEntityBeforeSave(XgStuColBatch entity) {
|
||||
// TODO 做一些数据校验,如唯一约束
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,8 +127,8 @@ public class XgStuColBatchServiceImpl implements IXgStuColBatchService {
|
||||
*/
|
||||
@Override
|
||||
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||
if(isValid){
|
||||
//TODO 做一些业务上的校验,判断是否需要校验
|
||||
if (isValid) {
|
||||
// TODO 做一些业务上的校验,判断是否需要校验
|
||||
}
|
||||
return baseMapper.deleteByIds(ids) > 0;
|
||||
}
|
||||
|
||||
+3
-1
@@ -98,6 +98,8 @@ public class XskhAssessmentScheduleServiceImpl implements IXskhAssessmentSchedul
|
||||
if (bo.getStatusList() != null && !bo.getStatusList().isEmpty()) {
|
||||
lqw.in(XskhAssessmentSchedule::getStatus, bo.getStatusList());
|
||||
}
|
||||
// 按创建时间倒序排列
|
||||
lqw.orderByDesc(XskhAssessmentSchedule::getCreateTime);
|
||||
return lqw;
|
||||
}
|
||||
|
||||
@@ -222,4 +224,4 @@ public class XskhAssessmentScheduleServiceImpl implements IXskhAssessmentSchedul
|
||||
schedule.setStatus("3"); // 设置为已结束
|
||||
return baseMapper.updateById(schedule) > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,12 @@
|
||||
## API模块列表
|
||||
- [学生信息管理API](./学生信息管理API.md)
|
||||
- [学生信息收集批次管理API](./学生信息收集批次管理API.md)
|
||||
- [学生信息收集批次API](./学生信息收集批次API.md) - **重要更新**:修改列表排序方式为按创建时间倒序(2025-01-03)
|
||||
- [班级信息管理API](./班级信息管理API.md)
|
||||
- [班级干部信息管理API](./班级干部信息管理API.md)
|
||||
- [宿舍管理API](./宿舍管理API.md)
|
||||
- [宿舍房间管理API](./宿舍房间管理API.md) - **新增床位详细信息查询接口**(2025-01-27)
|
||||
- [宿舍床位分配管理API](./宿舍床位分配管理API.md) - **重要修复:床位分配不完整问题**(2025-01-27)
|
||||
- [宿舍管理员信息API](./宿舍管理员信息API.md)
|
||||
- [宿舍管理员授权API](./宿舍管理员授权API.md)
|
||||
- [学生管理员授权关系API](./学生管理员授权关系API.md)
|
||||
@@ -82,6 +84,8 @@
|
||||
| 处理预约申请 | POST | /api/xljk/consultation/appointment/process | 预约管理 | xljk:consultation:appointment:process |
|
||||
| **获取房间床位详细信息** | GET | /dormbed/dormBed/details/room/{roomId} | 宿舍管理 | dormbed:dormBed:query |
|
||||
| **获取宿舍楼床位详细信息** | GET | /dormbed/dormBed/details/building/{buildingId} | 宿舍管理 | dormbed:dormBed:query |
|
||||
| **批量分配床位给班级** | POST | /dormBedStudent/dormBedStudent/batchAllocateToClass | 床位分配 | xgss:dormBedStudent:allocateToClass |
|
||||
| **批量分配床位给学院** | POST | /dormBedStudent/dormBedStudent/batchAllocateToCollege | 床位分配 | xgss:dormBedStudent:allocateToCollege |
|
||||
| 记录咨询结果 | POST | /api/xljk/consultation/appointment/record | 预约管理 | xljk:consultation:appointment:record |
|
||||
| 查询未到访记录列表 | GET | /api/xljk/consultation/no-show/list | 预约限制 | xljk:consultation:noshow:list |
|
||||
| 获取未到访记录详情 | GET | /api/xljk/consultation/no-show/{id} | 预约限制 | xljk:consultation:noshow:query |
|
||||
@@ -96,6 +100,7 @@
|
||||
| 2025-07-01 | 1.0 | 初始版本 | 系统管理员 |
|
||||
| 2025-07-15 | 1.1 | 添加心理健康测评API | 张工 |
|
||||
| 2025-07-30 | 1.2 | 添加预约限制功能API | Cursor |
|
||||
| 2025-01-27 | 1.3 | 添加宿舍床位分配管理API,修复床位分配不完整问题 | Cursor |
|
||||
|
||||
**详细文档**: [企业信息管理API.md](./企业信息管理API.md)
|
||||
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
# 学生信息收集批次API文档
|
||||
|
||||
## 📋 文档信息
|
||||
|
||||
| 项目 | 信息 |
|
||||
|------|------|
|
||||
| **项目名称** | 校园管理系统 - 学生信息收集批次 |
|
||||
| **API模块** | 学生信息收集批次管理 |
|
||||
| **文档版本** | V1.1.0 |
|
||||
| **创建时间** | 2025-01-03 |
|
||||
| **基础路径** | `/XgStuColBatch/stuColBatch` |
|
||||
|
||||
## 🎯 API概述
|
||||
|
||||
### 模块说明
|
||||
学生信息收集批次管理系统提供完整的批次创建、查询、修改、删除等API接口,支持学生信息收集的批次化管理。
|
||||
|
||||
### 技术规范
|
||||
- **认证方式**: Bearer Token (Sa-Token)
|
||||
- **数据格式**: JSON
|
||||
- **响应格式**: 统一使用R类型或TableDataInfo分页格式
|
||||
- **权限控制**: 基于角色的权限管理(RBAC)
|
||||
|
||||
### 权限角色
|
||||
- **学生管理员**: 查看权限范围内的批次信息
|
||||
- **班主任**: 查看相关班级的批次信息
|
||||
- **全校学生管理员**: 查看全校范围的批次信息
|
||||
- **系统管理员**: 完整的批次管理权限
|
||||
|
||||
## 📊 数据模型
|
||||
|
||||
### 学生信息收集批次VO (XgStuColBatchVo)
|
||||
|
||||
```java
|
||||
{
|
||||
"id": 1, // 批次ID
|
||||
"batchName": "2025年春季学期信息收集", // 批次名称
|
||||
"description": "收集学生基本信息", // 批次描述
|
||||
"startTime": "2025-02-01 00:00:00", // 开始时间
|
||||
"endTime": "2025-02-28 23:59:59", // 结束时间
|
||||
"status": "0", // 状态(0正常 1停用)
|
||||
"collectionType": "1", // 收集类型(1-新学期收集 2-其他)
|
||||
"remark": "备注信息", // 备注
|
||||
"createBy": 1, // 创建者
|
||||
"createTime": "2025-01-27 10:00:00", // 创建时间
|
||||
"updateBy": 1, // 更新者
|
||||
"updateTime": "2025-01-27 10:00:00" // 更新时间
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 API接口列表
|
||||
|
||||
### 1. 基础管理接口
|
||||
|
||||
#### 1.1 查询学生信息收集批次列表
|
||||
- **接口路径**: GET `/XgStuColBatch/stuColBatch/list`
|
||||
- **权限标识**: `XgStuColBatch:stuColBatch:list`
|
||||
- **功能描述**: 分页查询学生信息收集批次列表
|
||||
- **排序方式**: 按创建时间倒序排列(最新创建的批次在前)
|
||||
|
||||
**请求参数**:
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| pageNum | integer | 否 | 页码,默认1 |
|
||||
| pageSize | integer | 否 | 每页大小,默认10 |
|
||||
| batchName | string | 否 | 批次名称(模糊查询) |
|
||||
| status | string | 否 | 状态(0正常 1停用) |
|
||||
| collectionType | string | 否 | 收集类型(1-新学期收集 2-其他) |
|
||||
|
||||
**响应数据**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "查询成功",
|
||||
"rows": [
|
||||
{
|
||||
"id": 2,
|
||||
"batchName": "2025年春季学期信息收集",
|
||||
"description": "收集学生基本信息",
|
||||
"startTime": "2025-02-01 00:00:00",
|
||||
"endTime": "2025-02-28 23:59:59",
|
||||
"status": "0",
|
||||
"collectionType": "1",
|
||||
"createTime": "2025-01-27 15:30:00"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"batchName": "2024年秋季学期信息收集",
|
||||
"description": "收集学生基本信息",
|
||||
"startTime": "2024-09-01 00:00:00",
|
||||
"endTime": "2024-09-30 23:59:59",
|
||||
"status": "1",
|
||||
"collectionType": "1",
|
||||
"createTime": "2024-08-15 10:00:00"
|
||||
}
|
||||
],
|
||||
"total": 2
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.2 获取学生信息收集批次详细信息
|
||||
- **接口路径**: GET `/XgStuColBatch/stuColBatch/{id}`
|
||||
- **权限标识**: `XgStuColBatch:stuColBatch:query`
|
||||
- **功能描述**: 根据主键获取学生信息收集批次详细信息
|
||||
|
||||
**请求参数**:
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| id | long | 是 | 批次主键ID |
|
||||
|
||||
**响应数据**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "查询成功",
|
||||
"data": {
|
||||
"id": 1,
|
||||
"batchName": "2025年春季学期信息收集",
|
||||
"description": "收集学生基本信息",
|
||||
"startTime": "2025-02-01 00:00:00",
|
||||
"endTime": "2025-02-28 23:59:59",
|
||||
"status": "0",
|
||||
"collectionType": "1",
|
||||
"remark": "备注信息",
|
||||
"createBy": 1,
|
||||
"createTime": "2025-01-27 10:00:00",
|
||||
"updateBy": 1,
|
||||
"updateTime": "2025-01-27 10:00:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.3 新增学生信息收集批次
|
||||
- **接口路径**: POST `/XgStuColBatch/stuColBatch`
|
||||
- **权限标识**: `XgStuColBatch:stuColBatch:add`
|
||||
- **功能描述**: 新增学生信息收集批次
|
||||
|
||||
**请求参数**:
|
||||
```json
|
||||
{
|
||||
"batchName": "2025年春季学期信息收集",
|
||||
"description": "收集学生基本信息",
|
||||
"startTime": "2025-02-01 00:00:00",
|
||||
"endTime": "2025-02-28 23:59:59",
|
||||
"status": "0",
|
||||
"collectionType": "1",
|
||||
"remark": "备注信息"
|
||||
}
|
||||
```
|
||||
|
||||
**响应数据**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.4 修改学生信息收集批次
|
||||
- **接口路径**: PUT `/XgStuColBatch/stuColBatch`
|
||||
- **权限标识**: `XgStuColBatch:stuColBatch:edit`
|
||||
- **功能描述**: 修改学生信息收集批次信息
|
||||
|
||||
**请求参数**:
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"batchName": "2025年春季学期信息收集",
|
||||
"description": "收集学生基本信息",
|
||||
"startTime": "2025-02-01 00:00:00",
|
||||
"endTime": "2025-02-28 23:59:59",
|
||||
"status": "0",
|
||||
"collectionType": "1",
|
||||
"remark": "备注信息"
|
||||
}
|
||||
```
|
||||
|
||||
**响应数据**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.5 删除学生信息收集批次
|
||||
- **接口路径**: DELETE `/XgStuColBatch/stuColBatch/{ids}`
|
||||
- **权限标识**: `XgStuColBatch:stuColBatch:remove`
|
||||
- **功能描述**: 批量删除学生信息收集批次
|
||||
|
||||
**请求参数**:
|
||||
| 参数名 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|
|
||||
| ids | string | 是 | 批次ID集合,多个ID用逗号分隔 |
|
||||
|
||||
**响应数据**:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 1.6 导出学生信息收集批次
|
||||
- **接口路径**: POST `/XgStuColBatch/stuColBatch/export`
|
||||
- **权限标识**: `XgStuColBatch:stuColBatch:export`
|
||||
- **功能描述**: 导出学生信息收集批次列表到Excel
|
||||
|
||||
**请求参数**:
|
||||
同查询列表接口,用于筛选导出数据。
|
||||
|
||||
**响应数据**:
|
||||
返回Excel文件流。
|
||||
|
||||
## 📝 更新历史
|
||||
|
||||
| 日期 | 版本 | 更新内容 | 更新人 |
|
||||
|------|------|---------|-------|
|
||||
| 2025-01-03 | V1.1.0 | **重要更新**:修改列表接口排序方式,从按ID升序改为按创建时间倒序,确保最新创建的批次显示在前面 | 系统管理员 |
|
||||
| 2025-01-27 | V1.0.0 | 初始版本,创建学生信息收集批次管理API文档 | 系统管理员 |
|
||||
|
||||
## 🚨 重要说明
|
||||
|
||||
### 排序变更说明
|
||||
- **变更时间**: 2025-01-03
|
||||
- **变更内容**: 列表查询接口的排序方式从按ID升序(`orderByAsc(XgStuColBatch::getId)`)改为按创建时间倒序(`orderByDesc(XgStuColBatch::getCreateTime)`)
|
||||
- **变更原因**: 提升用户体验,确保最新创建的批次显示在列表前面,便于用户快速找到最近的批次
|
||||
- **影响范围**: 仅影响列表查询接口的数据排序,不影响其他功能
|
||||
|
||||
### 技术实现
|
||||
- **后端实现**: 修改`XgStuColBatchServiceImpl.buildQueryWrapper()`方法中的排序逻辑
|
||||
- **数据库字段**: 使用`create_time`字段进行排序
|
||||
- **继承关系**: `XgStuColBatch` → `TenantEntity` → `BaseEntity`(`createTime`字段在BaseEntity中定义)
|
||||
@@ -0,0 +1,300 @@
|
||||
# 宿舍床位分配管理API文档
|
||||
|
||||
## 目录
|
||||
|
||||
- [1. 接口概述](#1-接口概述)
|
||||
- [2. 批量分配床位给班级](#2-批量分配床位给班级)
|
||||
- [2.1 接口说明](#21-接口说明)
|
||||
- [2.2 重要修复记录](#22-重要修复记录)
|
||||
- [2.3 请求参数](#23-请求参数)
|
||||
- [2.4 返回结果](#24-返回结果)
|
||||
- [2.5 业务逻辑](#25-业务逻辑)
|
||||
- [3. 批量分配床位给学院](#3-批量分配床位给学院)
|
||||
|
||||
## 1. 接口概述
|
||||
|
||||
宿舍床位分配管理API提供了床位分配的核心功能,包括:
|
||||
- 批量分配床位给学院
|
||||
- 批量分配床位给班级(学院分配后的二次分配)
|
||||
- 床位收回和重新分配
|
||||
|
||||
## 2. 批量分配床位给班级
|
||||
|
||||
### 2.1 接口说明
|
||||
|
||||
该接口用于将已分配给学院的床位进一步分配给具体的班级。支持房间优先分配策略,尽量避免混住。
|
||||
|
||||
#### 请求方式
|
||||
|
||||
```
|
||||
POST /dormBedStudent/dormBedStudent/batchAllocateToClass
|
||||
```
|
||||
|
||||
#### 权限要求
|
||||
|
||||
```
|
||||
xgss:dormBedStudent:allocateToClass
|
||||
```
|
||||
|
||||
### 2.2 重要修复记录
|
||||
|
||||
**修复日期:2025-01-27**
|
||||
|
||||
#### 问题描述
|
||||
在床位分配过程中,当床位数量小于等于分配的数量时,存在部分床位未能成功分配的问题。具体现象:
|
||||
- 前端显示34个床位需要分配
|
||||
- 接口执行后,部分床位没有被分配给班级
|
||||
- 导致实际分配数量少于预期分配数量
|
||||
|
||||
#### 根本原因
|
||||
1. **分配算法与更新逻辑不匹配**:分配算法使用`roomUsage`数组记录房间使用情况,但更新逻辑依赖`roomClassMap`判断床位归属
|
||||
2. **混住场景下的床位丢失**:当房间产生混住时,`roomClassMap`只记录最后分配的班级,导致之前分配的床位在更新时被忽略
|
||||
3. **缺乏精确的床位级别跟踪**:原算法只在房间级别跟踪分配,没有在床位级别进行精确记录
|
||||
|
||||
#### 修复方案
|
||||
1. **新增床位级别映射**:引入`bedClassMap`(Map<Long, Long>)精确记录每个床位分配给哪个班级
|
||||
2. **四轮分配策略优化**:在每轮分配中同步更新`bedClassMap`,确保床位分配记录准确
|
||||
3. **更新逻辑重构**:使用`bedClassMap`替代原有的房间级别判断逻辑,确保所有分配的床位都能正确更新
|
||||
4. **完整性验证**:增加分配结果验证,确保预期分配数量与实际分配数量一致
|
||||
|
||||
#### 修复效果
|
||||
- ✅ 解决了床位分配不完整的问题
|
||||
- ✅ 确保34个床位全部正确分配
|
||||
- ✅ 支持混住场景下的精确床位跟踪
|
||||
- ✅ 增加了详细的日志记录和验证机制
|
||||
|
||||
### 2.3 请求参数
|
||||
|
||||
#### 请求体参数
|
||||
|
||||
```json
|
||||
{
|
||||
"bedIds": ["1947684655866236929", "1947684656965144578", ...],
|
||||
"classIds": ["1947944181282590721", "1947943837236416514", ...],
|
||||
"classStudentCounts": [0, 6, 7, 7, 3, 11],
|
||||
"buildingId": "1947658161827794945",
|
||||
"buildingType": "2"
|
||||
}
|
||||
```
|
||||
|
||||
#### 参数说明
|
||||
|
||||
| 参数名 | 类型 | 必须 | 描述 |
|
||||
|--------|------|------|------|
|
||||
| bedIds | Long[] | 是 | 需要分配的床位ID列表 |
|
||||
| classIds | Long[] | 是 | 目标班级ID列表 |
|
||||
| classStudentCounts | Integer[] | 否 | 各班级分配床位数量,与classIds一一对应 |
|
||||
| buildingId | Long | 是 | 楼栋ID |
|
||||
| buildingType | String | 是 | 楼栋类型(1-男生楼,2-女生楼) |
|
||||
|
||||
#### 参数校验规则
|
||||
|
||||
1. **床位状态校验**:所有床位必须已分配给学院且未分配给班级
|
||||
2. **数量校验**:classStudentCounts总和不能超过可用床位总数
|
||||
3. **班级信息校验**:所有classIds必须存在且有效
|
||||
4. **楼栋信息校验**:buildingId必须存在且与床位楼栋匹配
|
||||
|
||||
### 2.4 返回结果
|
||||
|
||||
#### 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功",
|
||||
"data": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 500,
|
||||
"msg": "床位分配失败:具体错误信息",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 常见错误信息
|
||||
|
||||
| 错误信息 | 原因 | 解决方案 |
|
||||
|----------|------|----------|
|
||||
| 以下床位已分配给班级,无法重复分配 | 床位已被其他班级占用 | 检查床位状态,选择未分配的床位 |
|
||||
| 选择的床位尚未分配给学院 | 床位未完成学院分配 | 先执行学院分配再进行班级分配 |
|
||||
| 班级人数总和超过了可用床位总数 | 分配数量超过可用床位 | 调整各班级分配数量 |
|
||||
| 床位分配不完整,存在未分配的床位 | 分配算法异常 | 联系技术支持检查日志 |
|
||||
|
||||
### 2.5 业务逻辑
|
||||
|
||||
#### 分配策略
|
||||
|
||||
**四轮房间优先分配策略**:
|
||||
|
||||
1. **第一轮:完整房间分配**
|
||||
- 优先将空闲房间完整分配给单个班级
|
||||
- 条件:房间空闲 && 班级人数 >= 房间床位数
|
||||
|
||||
2. **第二轮:同班级房间补充**
|
||||
- 在已分配给该班级的房间中补充床位
|
||||
- 避免不必要的混住
|
||||
|
||||
3. **第三轮:空闲房间分配**
|
||||
- 将剩余学生分配到完全空闲的房间
|
||||
- 保持房间内班级单一性
|
||||
|
||||
4. **第四轮:混住分配**
|
||||
- 将剩余学生分配到任何有空闲床位的房间
|
||||
- 允许混住以确保所有学生都有床位
|
||||
|
||||
#### 分配原则
|
||||
|
||||
- **房间优先**:尽量保持同一房间内学生来自同一班级
|
||||
- **顺序分配**:按房间编码顺序进行分配
|
||||
- **完整性保证**:确保所有指定的床位都被分配
|
||||
- **日志记录**:详细记录分配过程和结果
|
||||
|
||||
#### 数据更新
|
||||
|
||||
分配完成后,系统会更新以下信息:
|
||||
- `xg_dorm_bed_student.class_id`:床位分配的班级ID
|
||||
- `xg_dorm_bed_student.major_id`:床位分配的专业ID
|
||||
- `xg_dorm_bed_student.update_time`:更新时间
|
||||
- `xg_dorm_bed_student.update_by`:更新人员
|
||||
|
||||
## 3. 批量分配床位给学院
|
||||
|
||||
### 3.1 接口说明
|
||||
|
||||
该接口用于将空闲床位批量分配给二级学院,是床位分配的第一步。
|
||||
|
||||
#### 请求方式
|
||||
|
||||
```
|
||||
POST /dormBedStudent/dormBedStudent/batchAllocateToCollege
|
||||
```
|
||||
|
||||
#### 权限要求
|
||||
|
||||
```
|
||||
xgss:dormBedStudent:allocateToCollege
|
||||
```
|
||||
|
||||
#### 请求参数
|
||||
|
||||
```json
|
||||
{
|
||||
"bedIds": ["床位ID列表"],
|
||||
"collegeIds": ["学院ID列表"],
|
||||
"collegeStudentCounts": [各学院分配数量],
|
||||
"buildingId": "楼栋ID",
|
||||
"buildingType": "楼栋类型"
|
||||
}
|
||||
```
|
||||
|
||||
#### 业务逻辑
|
||||
|
||||
1. **床位状态校验**:确保床位处于空闲状态(bed_status = "0")
|
||||
2. **学院分配**:采用与班级分配相同的房间优先策略
|
||||
3. **数据更新**:更新床位的学院归属信息
|
||||
|
||||
---
|
||||
|
||||
## 技术说明
|
||||
|
||||
### 关键数据结构
|
||||
|
||||
```java
|
||||
// 房间使用状态跟踪
|
||||
int[] roomUsage = new int[sortedRoomIds.size()];
|
||||
|
||||
// 房间主要归属班级映射
|
||||
Map<Integer, Long> roomClassMap = new HashMap<>();
|
||||
|
||||
// 床位精确分配映射(修复关键)
|
||||
Map<Long, Long> bedClassMap = new HashMap<>(); // bedId -> classId
|
||||
```
|
||||
|
||||
### 修复前后对比
|
||||
|
||||
**修复前**:
|
||||
- 依赖房间级别的`roomClassMap`判断床位归属
|
||||
- 混住场景下床位分配记录丢失
|
||||
- 更新逻辑与分配算法不匹配
|
||||
|
||||
**修复后**:
|
||||
- 使用床位级别的`bedClassMap`精确跟踪
|
||||
- 支持混住场景下的完整床位分配
|
||||
- 分配算法与更新逻辑完全一致
|
||||
|
||||
### 性能优化
|
||||
|
||||
- 使用批量更新减少数据库操作
|
||||
- 流式处理大量床位数据
|
||||
- 详细的日志记录便于问题排查
|
||||
|
||||
---
|
||||
|
||||
## 修复记录
|
||||
|
||||
### 2025-01-27 第二轮修复:完全匹配场景下的床位分配问题
|
||||
|
||||
**严重问题**:
|
||||
用户反馈75个床位完全匹配75个需求时,仍有13个床位未分配。
|
||||
|
||||
**新发现的根本原因**:
|
||||
1. **床位预筛选逻辑错误**:条件 `.isNull(XgDormBedStudent::getClassId)` 导致已分配班级的床位被排除在分配范围之外
|
||||
2. **数据库更新条件过严**:只更新未分配班级的床位,忽略了需要重新分配的床位
|
||||
3. **验证逻辑不够严格**:对完全匹配场景缺少强制验证
|
||||
|
||||
**第二轮修复方案**:
|
||||
|
||||
#### 1. 修复床位预筛选逻辑
|
||||
```java
|
||||
// 修复前:只查询未分配班级的床位
|
||||
.isNull(XgDormBedStudent::getClassId) // 只分配了学院,还没分配班级
|
||||
|
||||
// 修复后:查询所有已分配给学院的床位,不论是否已分配班级
|
||||
// 移除了.isNull(XgDormBedStudent::getClassId)条件
|
||||
```
|
||||
|
||||
#### 2. 增强数据库更新策略
|
||||
```java
|
||||
// 分两种情况处理:
|
||||
// 情况1:更新未分配班级的记录(新分配)
|
||||
// 情况2:强制更新所有指定床位的班级分配(重新分配)
|
||||
int totalUpdateCount = updateCount1 + updateCount2;
|
||||
```
|
||||
|
||||
#### 3. 完善验证和诊断机制
|
||||
```java
|
||||
// 当床位数量完全匹配需求时,必须全部分配完
|
||||
if (totalExpectedBeds == totalAvailableBeds && totalAllocatedBeds < totalExpectedBeds) {
|
||||
// 详细诊断未分配的床位和房间分布
|
||||
throw new RuntimeException("床位分配不完整,存在" + (totalExpectedBeds - totalAllocatedBeds) + "个床位未分配");
|
||||
}
|
||||
```
|
||||
|
||||
### 2025-01-27 第一轮修复:床位分配不完整问题
|
||||
|
||||
**问题描述**:
|
||||
在床位分配过程中,存在剩余床位未分配的问题。例如:
|
||||
- 34个床位分配给6个班级时,存在剩余床位未分配
|
||||
- 分配算法与更新逻辑不匹配,导致混住场景下床位分配记录丢失
|
||||
|
||||
**根本原因**:
|
||||
1. **分配算法使用房间级别跟踪**:`roomUsage`数组 + `roomClassMap`映射
|
||||
2. **更新逻辑依赖房间归属判断**:只处理`roomClassMap`中记录的房间
|
||||
3. **混住场景下的数据丢失**:`roomClassMap`被覆盖,之前分配的床位被忽略
|
||||
|
||||
**第一轮修复方案**:
|
||||
- 新增床位级别精确跟踪:`Map<Long, Long> bedClassMap`
|
||||
- 四轮分配策略优化:同步更新床位分配记录
|
||||
- 更新逻辑重构:使用`bedClassMap`替代房间级别判断
|
||||
- 完整性验证:确保所有床位都能正确分配
|
||||
|
||||
---
|
||||
|
||||
**文档版本**:v1.2
|
||||
**最后更新**:2025-01-27
|
||||
**修复版本**:重要Bug修复 - 完全匹配场景下床位分配不完整问题(第二轮修复)
|
||||
@@ -50,7 +50,7 @@
|
||||
<!-- 流程列表 -->
|
||||
<view v-else class="process-list">
|
||||
<view
|
||||
v-for="item in processInstanceList"
|
||||
v-for="(item, index) in processInstanceList"
|
||||
:key="item.id"
|
||||
class="process-item"
|
||||
@click="handleView(item)"
|
||||
@@ -58,6 +58,7 @@
|
||||
<!-- 流程信息 -->
|
||||
<view class="process-header">
|
||||
<view class="process-title-row">
|
||||
<text class="title-index">{{ index + 1 }}.</text>
|
||||
<text class="process-name">{{ item.flowName }}</text>
|
||||
<u-tag
|
||||
:text="getStatusText(item.flowStatus)"
|
||||
@@ -751,6 +752,12 @@ export default {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.title-index {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.process-name {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
|
||||
@@ -43,7 +43,10 @@
|
||||
<view v-for="(item, index) in taskList" :key="item.id || index" class="task-card">
|
||||
<!-- 卡片头部 -->
|
||||
<view class="card-header">
|
||||
<view class="flow-name">{{ item.flowName || '-' }}</view>
|
||||
<view class="flow-name">
|
||||
<text class="title-index">{{ index + 1 }}.</text>
|
||||
{{ item.flowName || '-' }}
|
||||
</view>
|
||||
<view class="flow-status">
|
||||
<u-tag
|
||||
:text="getFlowStatusText(item.flowStatus)"
|
||||
@@ -458,6 +461,12 @@ export default {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.title-index {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.flow-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
|
||||
@@ -73,7 +73,10 @@
|
||||
>
|
||||
<!-- 卡片头部 -->
|
||||
<view class="card-header">
|
||||
<view class="task-title">{{ item.flowName || '-' }}</view>
|
||||
<view class="task-title">
|
||||
<text class="title-index">{{ index + 1 }}.</text>
|
||||
{{ item.flowName || '-' }}
|
||||
</view>
|
||||
<view class="task-status">
|
||||
<u-tag
|
||||
:text="getFlowStatusText(item.flowStatus)"
|
||||
@@ -842,6 +845,12 @@ export default {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.title-index {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.task-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
|
||||
@@ -53,92 +53,110 @@
|
||||
<u-empty text="暂无待办任务" mode="data"></u-empty>
|
||||
</view>
|
||||
|
||||
<!-- 任务卡片列表 -->
|
||||
<view v-else class="task-list">
|
||||
<view v-for="(item, index) in taskList" :key="item.id || index" class="task-card">
|
||||
<!-- 卡片头部 -->
|
||||
<view class="card-header">
|
||||
<view class="task-title">{{ item.flowName || '-' }}</view>
|
||||
<view class="task-status">
|
||||
<u-tag
|
||||
:text="getFlowStatusText(item.flowStatus)"
|
||||
:type="getFlowStatusType(item.flowStatus)"
|
||||
size="mini"
|
||||
></u-tag>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片内容 -->
|
||||
<view class="card-content">
|
||||
<view class="info-item">
|
||||
<text class="label">流程名称:</text>
|
||||
<text class="value">{{ item.flowName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">流程分类:</text>
|
||||
<text class="value">{{ item.categoryName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">任务名称:</text>
|
||||
<text class="value">{{ item.nodeName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">申请人:</text>
|
||||
<text class="value">{{ item.createByName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">办理人:</text>
|
||||
<view class="assignee-tags">
|
||||
<template v-if="item.assigneeNames">
|
||||
<u-tag
|
||||
v-for="(name, idx) in item.assigneeNames.split(',')"
|
||||
:key="idx"
|
||||
:text="name"
|
||||
type="success"
|
||||
size="mini"
|
||||
style="margin-right: 8rpx; margin-bottom: 8rpx"
|
||||
></u-tag>
|
||||
</template>
|
||||
<u-tag v-else text="无" type="info" size="mini"></u-tag>
|
||||
<!-- 任务卡片列表 - 滚动加载 -->
|
||||
<scroll-view
|
||||
v-else
|
||||
scroll-y
|
||||
class="task-scroll-list"
|
||||
@scrolltolower="loadMore"
|
||||
:scroll-top="scrollTop"
|
||||
refresher-enabled="true"
|
||||
:refresher-triggered="refreshing"
|
||||
@refresherrefresh="onRefresh"
|
||||
>
|
||||
<view class="task-list">
|
||||
<view
|
||||
v-for="(item, index) in taskList"
|
||||
:key="item.id || index"
|
||||
class="task-card"
|
||||
>
|
||||
<!-- 卡片头部 -->
|
||||
<view class="card-header">
|
||||
<view class="task-title">
|
||||
<text class="title-index">{{ index + 1 }}.</text>
|
||||
{{ item.flowName || '-' }}
|
||||
</view>
|
||||
<view class="task-status">
|
||||
<u-tag
|
||||
:text="getFlowStatusText(item.flowStatus)"
|
||||
:type="getFlowStatusType(item.flowStatus)"
|
||||
size="mini"
|
||||
></u-tag>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">创建时间:</text>
|
||||
<text class="value">{{ formatTime(item.createTime) }}</text>
|
||||
|
||||
<!-- 卡片内容 -->
|
||||
<view class="card-content">
|
||||
<view class="info-item">
|
||||
<text class="label">流程名称:</text>
|
||||
<text class="value">{{ item.flowName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">流程分类:</text>
|
||||
<text class="value">{{ item.categoryName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">任务名称:</text>
|
||||
<text class="value">{{ item.nodeName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">申请人:</text>
|
||||
<text class="value">{{ item.createByName || '-' }}</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">办理人:</text>
|
||||
<view class="assignee-tags">
|
||||
<template v-if="item.assigneeNames">
|
||||
<u-tag
|
||||
v-for="(name, idx) in item.assigneeNames.split(',')"
|
||||
:key="idx"
|
||||
:text="name"
|
||||
type="success"
|
||||
size="mini"
|
||||
style="margin-right: 8rpx; margin-bottom: 8rpx"
|
||||
></u-tag>
|
||||
</template>
|
||||
<u-tag v-else text="无" type="info" size="mini"></u-tag>
|
||||
</view>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="label">创建时间:</text>
|
||||
<text class="value">{{ formatTime(item.createTime) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="card-actions">
|
||||
<u-button
|
||||
@click="handleApproval(item)"
|
||||
type="primary"
|
||||
size="small"
|
||||
shape="circle"
|
||||
>
|
||||
<u-icon
|
||||
name="edit-pen"
|
||||
size="16"
|
||||
color="#fff"
|
||||
style="margin-right: 8rpx"
|
||||
></u-icon>
|
||||
办理
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="card-actions">
|
||||
<u-button
|
||||
@click="handleApproval(item)"
|
||||
type="primary"
|
||||
size="small"
|
||||
shape="circle"
|
||||
>
|
||||
<u-icon
|
||||
name="edit-pen"
|
||||
size="16"
|
||||
color="#fff"
|
||||
style="margin-right: 8rpx"
|
||||
></u-icon>
|
||||
办理
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 滚动加载更多 -->
|
||||
<u-loadmore
|
||||
v-if="taskList.length > 0"
|
||||
:status="loadmoreStatus"
|
||||
:load-text="{
|
||||
loadmore: '轻轻上拉',
|
||||
loading: '努力加载中',
|
||||
nomore: '没有更多了',
|
||||
}"
|
||||
@loadmore="loadMore"
|
||||
></u-loadmore>
|
||||
<!-- 滚动加载更多状态 -->
|
||||
<view v-if="taskList.length > 0" class="loadmore-status">
|
||||
<u-loadmore
|
||||
:status="loadmoreStatus"
|
||||
:load-text="{
|
||||
loadmore: '轻轻上拉加载更多',
|
||||
loading: '努力加载中...',
|
||||
nomore: '没有更多数据了',
|
||||
}"
|
||||
></u-loadmore>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 申请人选择弹窗 -->
|
||||
@@ -229,6 +247,10 @@ export default {
|
||||
total: 0,
|
||||
loadmoreStatus: 'loadmore', // loadmore, loading, nomore
|
||||
|
||||
// 滚动相关
|
||||
scrollTop: 0,
|
||||
refreshing: false,
|
||||
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
@@ -262,6 +284,8 @@ export default {
|
||||
|
||||
async created() {
|
||||
await this.loadFlowStatusDict();
|
||||
// 重置滚动位置
|
||||
this.scrollTop = 0;
|
||||
await this.getList();
|
||||
},
|
||||
|
||||
@@ -320,6 +344,8 @@ export default {
|
||||
if (!isLoadMore) {
|
||||
this.loading = true;
|
||||
this.queryParams.pageNum = 1;
|
||||
// 非加载更多时重置滚动位置
|
||||
this.scrollTop = 0;
|
||||
} else {
|
||||
this.loadmoreStatus = 'loading';
|
||||
}
|
||||
@@ -400,6 +426,8 @@ export default {
|
||||
|
||||
// 搜索操作
|
||||
handleQuery() {
|
||||
// 重置滚动位置
|
||||
this.scrollTop = 0;
|
||||
this.getList();
|
||||
},
|
||||
|
||||
@@ -413,6 +441,35 @@ export default {
|
||||
this.getList(true);
|
||||
},
|
||||
|
||||
// 下拉刷新
|
||||
async onRefresh() {
|
||||
this.refreshing = true;
|
||||
try {
|
||||
// 重置分页参数
|
||||
this.queryParams.pageNum = 1;
|
||||
this.loadmoreStatus = 'loadmore';
|
||||
|
||||
// 重新加载数据
|
||||
await this.getList();
|
||||
|
||||
// 显示刷新成功提示
|
||||
uni.showToast({
|
||||
title: '刷新成功',
|
||||
icon: 'success',
|
||||
duration: 1500,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('下拉刷新失败:', error);
|
||||
uni.showToast({
|
||||
title: '刷新失败',
|
||||
icon: 'none',
|
||||
duration: 1500,
|
||||
});
|
||||
} finally {
|
||||
this.refreshing = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 格式化时间
|
||||
formatTime(timeStr) {
|
||||
if (!timeStr) return '-';
|
||||
@@ -536,6 +593,8 @@ export default {
|
||||
}
|
||||
|
||||
this.userSelectDialog.visible = false;
|
||||
// 重置滚动位置并查询
|
||||
this.scrollTop = 0;
|
||||
this.handleQuery();
|
||||
},
|
||||
|
||||
@@ -717,6 +776,12 @@ export default {
|
||||
|
||||
.content-wrapper {
|
||||
padding: 20rpx;
|
||||
height: calc(100vh - 200rpx); /* 减去导航栏和搜索区域的高度 */
|
||||
}
|
||||
|
||||
.task-scroll-list {
|
||||
height: 100%;
|
||||
padding: 0 20rpx;
|
||||
}
|
||||
|
||||
.empty-container {
|
||||
@@ -744,6 +809,12 @@ export default {
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.title-index {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.task-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
@@ -923,4 +994,9 @@ export default {
|
||||
::v-deep .u-loadmore {
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
|
||||
.loadmore-status {
|
||||
padding: 20rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -92,6 +92,12 @@
|
||||
<!-- 请假记录卡片列表 -->
|
||||
<view v-else class="leave-list">
|
||||
<view v-for="(item, index) in leaveList" :key="item.id || index" class="leave-card">
|
||||
<!-- 序号标识 -->
|
||||
<view class="card-index">
|
||||
<text class="index-text">
|
||||
{{ getItemIndex(index) }}
|
||||
</text>
|
||||
</view>
|
||||
<!-- 卡片头部 -->
|
||||
<view class="card-header">
|
||||
<view class="leave-info">
|
||||
@@ -478,7 +484,11 @@ export default {
|
||||
本次加载: newData.length,
|
||||
总数: this.total,
|
||||
列表长度: this.leaveList.length,
|
||||
分页状态: this.loadStatus,
|
||||
});
|
||||
|
||||
// 验证序号连续性
|
||||
this.validateIndexContinuity();
|
||||
} else {
|
||||
console.warn('获取请假记录失败:', res?.msg);
|
||||
uni.showToast({
|
||||
@@ -518,7 +528,12 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('📄 开始加载更多数据...');
|
||||
console.log('📄 开始加载更多数据...', {
|
||||
当前页码: this.queryParams.pageNum,
|
||||
当前列表长度: this.leaveList.length,
|
||||
总数: this.total,
|
||||
});
|
||||
|
||||
this.queryParams.pageNum += 1;
|
||||
await this.loadLeaveList(false);
|
||||
},
|
||||
@@ -604,11 +619,17 @@ export default {
|
||||
// 搜索操作
|
||||
handleQuery() {
|
||||
console.log('🔍 执行搜索:', this.queryParams);
|
||||
|
||||
// 搜索时重置分页状态
|
||||
this.queryParams.pageNum = 1;
|
||||
this.loadStatus = 'loadmore';
|
||||
|
||||
this.loadLeaveList();
|
||||
},
|
||||
|
||||
// 重置搜索
|
||||
resetQuery() {
|
||||
console.log('🔄 重置搜索条件...');
|
||||
this.queryParams.startTime = '';
|
||||
this.queryParams.endTime = '';
|
||||
this.queryParams.approvalStatus = '';
|
||||
@@ -616,6 +637,12 @@ export default {
|
||||
this.endTimeDisplay = '';
|
||||
this.startTimeValue = '';
|
||||
this.endTimeValue = '';
|
||||
|
||||
// 重置分页状态
|
||||
this.queryParams.pageNum = 1;
|
||||
this.loadStatus = 'loadmore';
|
||||
|
||||
console.log('🔄 重置完成,开始加载数据...');
|
||||
this.loadLeaveList();
|
||||
},
|
||||
|
||||
@@ -703,6 +730,60 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 计算列表项序号
|
||||
getItemIndex(index) {
|
||||
// 序号应该基于数据在列表中的实际位置,而不是基于当前请求的页码
|
||||
// 直接使用 index + 1,因为数据已经按顺序合并到 leaveList 中
|
||||
const finalIndex = index + 1;
|
||||
|
||||
// 调试信息
|
||||
console.log('🔢 序号计算:', {
|
||||
当前项索引: index,
|
||||
最终序号: finalIndex,
|
||||
列表总长度: this.leaveList.length,
|
||||
});
|
||||
|
||||
return finalIndex;
|
||||
},
|
||||
|
||||
// 验证序号连续性
|
||||
validateIndexContinuity() {
|
||||
if (this.leaveList.length === 0) return;
|
||||
|
||||
console.log('🔍 验证序号连续性...');
|
||||
const expectedIndices = [];
|
||||
|
||||
for (let i = 0; i < this.leaveList.length; i++) {
|
||||
const expectedIndex = i + 1;
|
||||
const actualIndex = this.getItemIndex(i);
|
||||
expectedIndices.push({ position: i, expected: expectedIndex, actual: actualIndex });
|
||||
}
|
||||
|
||||
console.log('🔍 序号验证结果:', expectedIndices);
|
||||
|
||||
// 检查是否有不连续的序号
|
||||
const hasDiscontinuity = expectedIndices.some((item, index) => {
|
||||
if (index === 0) return false;
|
||||
return item.expected !== expectedIndices[index - 1].expected + 1;
|
||||
});
|
||||
|
||||
if (hasDiscontinuity) {
|
||||
console.warn('⚠️ 发现序号不连续:', expectedIndices);
|
||||
} else {
|
||||
console.log('✅ 序号连续性验证通过');
|
||||
}
|
||||
|
||||
// 额外验证:检查序号范围
|
||||
const minIndex = Math.min(...expectedIndices.map((item) => item.actual));
|
||||
const maxIndex = Math.max(...expectedIndices.map((item) => item.actual));
|
||||
console.log('🔍 序号范围验证:', {
|
||||
最小序号: minIndex,
|
||||
最大序号: maxIndex,
|
||||
期望范围: `1-${this.leaveList.length}`,
|
||||
是否正常: minIndex === 1 && maxIndex === this.leaveList.length,
|
||||
});
|
||||
},
|
||||
|
||||
// 查看详情
|
||||
handleViewDetail(item) {
|
||||
const detailInfo = [
|
||||
@@ -863,6 +944,26 @@ export default {
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
// 序号标识
|
||||
.card-index {
|
||||
position: absolute;
|
||||
top: 8rpx;
|
||||
left: 8rpx;
|
||||
background-color: #f5f6fa;
|
||||
padding: 6rpx 12rpx;
|
||||
border-radius: 8rpx;
|
||||
z-index: 1;
|
||||
min-width: 32rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.index-text {
|
||||
font-size: 24rpx;
|
||||
color: #909399;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
// 卡片头部
|
||||
@@ -871,6 +972,7 @@ export default {
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
padding: 24rpx;
|
||||
padding-left: 80rpx; // 为序号预留左边空间
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
|
||||
}
|
||||
@@ -928,6 +1030,7 @@ export default {
|
||||
// 卡片内容
|
||||
.card-content {
|
||||
padding: 24rpx;
|
||||
padding-left: 80rpx; // 与头部对齐
|
||||
}
|
||||
|
||||
.info-section {
|
||||
@@ -1021,6 +1124,7 @@ export default {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 20rpx;
|
||||
padding-left: 80rpx; // 与头部对齐
|
||||
border-top: 1px solid #f0f0f0;
|
||||
gap: 16rpx;
|
||||
}
|
||||
@@ -1080,8 +1184,21 @@ export default {
|
||||
}
|
||||
|
||||
.card-header,
|
||||
.card-content {
|
||||
.card-content,
|
||||
.card-actions {
|
||||
padding: 20rpx;
|
||||
padding-left: 70rpx; // 小屏幕下为序号预留左边空间
|
||||
}
|
||||
|
||||
.card-index {
|
||||
top: 6rpx;
|
||||
left: 6rpx;
|
||||
padding: 4rpx 8rpx;
|
||||
min-width: 28rpx;
|
||||
}
|
||||
|
||||
.index-text {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.fab-button {
|
||||
|
||||
@@ -39,7 +39,10 @@
|
||||
>
|
||||
<!-- 卡片头部 -->
|
||||
<view class="card-header">
|
||||
<view class="batch-title">{{ item.batchName || '-' }}</view>
|
||||
<view class="batch-title">
|
||||
<text class="batch-number">{{ index + 1 }}.</text>
|
||||
<text>{{ item.batchName || '-' }}</text>
|
||||
</view>
|
||||
<view class="batch-status">
|
||||
<u-tag
|
||||
:text="item.status === '0' ? '正常' : '停用'"
|
||||
@@ -90,7 +93,12 @@
|
||||
></u-icon>
|
||||
信息完善
|
||||
</u-button>
|
||||
<u-button type="info" shape="round" size="small" @click="handleViewHistory(item)">
|
||||
<u-button
|
||||
type="info"
|
||||
shape="round"
|
||||
size="small"
|
||||
@click="handleViewHistory(item)"
|
||||
>
|
||||
<u-icon
|
||||
name="clock"
|
||||
size="14"
|
||||
@@ -450,6 +458,13 @@ export default {
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.batch-number {
|
||||
color: #000000;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.batch-status {
|
||||
|
||||
@@ -50,12 +50,15 @@
|
||||
</view>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<u-loading-page :loading="loading" loading-text="加载中..."></u-loading-page>
|
||||
<u-loading-page
|
||||
:loading="loading && queryParams.pageNum === 1"
|
||||
loading-text="加载中..."
|
||||
></u-loading-page>
|
||||
|
||||
<!-- 列表内容 -->
|
||||
<view v-if="!loading" class="content-wrapper">
|
||||
<view v-if="!loading || queryParams.pageNum > 1" class="content-wrapper">
|
||||
<!-- 空状态 -->
|
||||
<view v-if="dormViolationList.length === 0" class="empty-container">
|
||||
<view v-if="dormViolationList.length === 0 && !loading" class="empty-container">
|
||||
<u-empty text="暂无违纪记录" mode="data"></u-empty>
|
||||
</view>
|
||||
|
||||
@@ -66,6 +69,13 @@
|
||||
:key="item.id || index"
|
||||
class="violation-card"
|
||||
>
|
||||
<!-- 序号标识 -->
|
||||
<view class="card-index">
|
||||
<text class="index-text">
|
||||
{{ index + 1 }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 卡片头部 -->
|
||||
<view class="card-header">
|
||||
<view class="student-info">
|
||||
@@ -178,15 +188,18 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载更多 -->
|
||||
<view v-if="hasMore" class="load-more">
|
||||
<u-loadmore
|
||||
:status="loadMoreStatus"
|
||||
@loadmore="loadMore"
|
||||
:loading-text="loadingText"
|
||||
:loadmore-text="loadMoreText"
|
||||
:nomore-text="noMoreText"
|
||||
></u-loadmore>
|
||||
<!-- 底部加载状态提示 -->
|
||||
<view v-if="dormViolationList.length > 0" class="load-status">
|
||||
<view v-if="loading && queryParams.pageNum > 1" class="loading-tip">
|
||||
<u-loading-icon mode="circle" size="32"></u-loading-icon>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
<view v-else-if="!hasMore" class="no-more-tip">
|
||||
<text class="no-more-text">没有更多数据了</text>
|
||||
</view>
|
||||
<view v-else-if="dormViolationList.length >= 10" class="pull-tip">
|
||||
<text class="pull-text">上拉加载更多</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -205,10 +218,6 @@ export default {
|
||||
dormViolationList: [],
|
||||
total: 0,
|
||||
hasMore: true,
|
||||
loadMoreStatus: 'loadmore',
|
||||
loadingText: '努力加载中...',
|
||||
loadMoreText: '点击或上拉加载更多',
|
||||
noMoreText: '没有更多了',
|
||||
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
@@ -253,6 +262,16 @@ export default {
|
||||
await this.initData();
|
||||
},
|
||||
|
||||
// 监听页面触底事件
|
||||
onReachBottom() {
|
||||
this.loadMore();
|
||||
},
|
||||
|
||||
// 监听下拉刷新事件
|
||||
onPullDownRefresh() {
|
||||
this.refreshData();
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 返回上一页
|
||||
goBack() {
|
||||
@@ -296,10 +315,19 @@ export default {
|
||||
|
||||
// 获取违纪记录列表
|
||||
async getList(isLoadMore = false) {
|
||||
if (this.loading) return;
|
||||
// 防止重复加载
|
||||
if (this.loading) {
|
||||
console.log('正在加载中,跳过本次请求');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果没有更多数据且是加载更多操作,直接返回
|
||||
if (isLoadMore && !this.hasMore) {
|
||||
console.log('没有更多数据,跳过加载更多');
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
this.loadMoreStatus = 'loading';
|
||||
|
||||
try {
|
||||
// 获取当前登录用户信息
|
||||
@@ -326,24 +354,52 @@ export default {
|
||||
if (res && res.code === 200) {
|
||||
const { rows, total } = res;
|
||||
|
||||
// 确保 rows 是数组
|
||||
const newData = Array.isArray(rows) ? rows : [];
|
||||
|
||||
console.log('API返回数据:', {
|
||||
isLoadMore: isLoadMore,
|
||||
pageNum: this.queryParams.pageNum,
|
||||
apiReturnedCount: newData.length,
|
||||
currentListLength: this.dormViolationList.length,
|
||||
total: total,
|
||||
});
|
||||
|
||||
if (isLoadMore) {
|
||||
this.dormViolationList = [...this.dormViolationList, ...rows];
|
||||
// 加载更多时,追加数据(去重处理)
|
||||
const existingIds = new Set(this.dormViolationList.map((item) => item.id));
|
||||
const uniqueNewData = newData.filter((item) => !existingIds.has(item.id));
|
||||
|
||||
console.log('加载更多去重处理:', {
|
||||
existingCount: this.dormViolationList.length,
|
||||
newDataCount: newData.length,
|
||||
uniqueNewDataCount: uniqueNewData.length,
|
||||
duplicateCount: newData.length - uniqueNewData.length,
|
||||
});
|
||||
|
||||
this.dormViolationList = [...this.dormViolationList, ...uniqueNewData];
|
||||
} else {
|
||||
this.dormViolationList = rows;
|
||||
// 首次加载或搜索时,替换数据
|
||||
this.dormViolationList = newData;
|
||||
}
|
||||
|
||||
this.total = total;
|
||||
|
||||
// 更新是否还有更多数据的状态
|
||||
this.hasMore = this.dormViolationList.length < total;
|
||||
|
||||
console.log('获取违纪记录成功:', {
|
||||
current: this.dormViolationList.length,
|
||||
finalListLength: this.dormViolationList.length,
|
||||
total: total,
|
||||
hasMore: this.hasMore,
|
||||
isLoadMore: isLoadMore,
|
||||
pageNum: this.queryParams.pageNum,
|
||||
});
|
||||
} else {
|
||||
if (!isLoadMore) {
|
||||
this.dormViolationList = [];
|
||||
this.total = 0;
|
||||
this.hasMore = false;
|
||||
}
|
||||
uni.showToast({
|
||||
title: res?.msg || '获取数据失败',
|
||||
@@ -355,6 +411,7 @@ export default {
|
||||
if (!isLoadMore) {
|
||||
this.dormViolationList = [];
|
||||
this.total = 0;
|
||||
this.hasMore = false;
|
||||
}
|
||||
uni.showToast({
|
||||
title: '获取数据失败',
|
||||
@@ -362,13 +419,17 @@ export default {
|
||||
});
|
||||
} finally {
|
||||
this.loading = false;
|
||||
this.loadMoreStatus = this.hasMore ? 'loadmore' : 'nomore';
|
||||
// 结束下拉刷新
|
||||
if (!isLoadMore) {
|
||||
uni.stopPullDownRefresh();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 搜索操作
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.hasMore = true;
|
||||
this.getList();
|
||||
},
|
||||
|
||||
@@ -379,10 +440,18 @@ export default {
|
||||
pageSize: 10,
|
||||
violationTime: '',
|
||||
};
|
||||
this.hasMore = true;
|
||||
this.getList();
|
||||
},
|
||||
|
||||
// 加载更多
|
||||
// 下拉刷新
|
||||
refreshData() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.hasMore = true;
|
||||
this.getList();
|
||||
},
|
||||
|
||||
// 加载更多(滚动到底部触发)
|
||||
loadMore() {
|
||||
if (this.hasMore && !this.loading) {
|
||||
this.queryParams.pageNum++;
|
||||
@@ -554,13 +623,34 @@ export default {
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card-index {
|
||||
position: absolute;
|
||||
top: 12rpx;
|
||||
left: 12rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background-color: rgba(162, 161, 161, 0.8);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.index-text {
|
||||
font-size: 22rpx;
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx;
|
||||
padding: 24rpx 24rpx 24rpx 70rpx;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
@@ -673,10 +763,42 @@ export default {
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.load-more {
|
||||
// 底部加载状态样式
|
||||
.load-status {
|
||||
padding: 30rpx 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loading-tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 26rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.no-more-tip {
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.no-more-text {
|
||||
font-size: 26rpx;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.pull-tip {
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.pull-text {
|
||||
font-size: 26rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
// uview组件样式调整
|
||||
::v-deep .u-tag {
|
||||
margin: 0;
|
||||
@@ -684,13 +806,11 @@ export default {
|
||||
|
||||
::v-deep .u-button {
|
||||
flex: 1;
|
||||
// max-width: 200rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
// 搜索按钮样式
|
||||
::v-deep .search-btn {
|
||||
// background: linear-gradient(135deg, #007aff, #0051d9) !important;
|
||||
border: none !important;
|
||||
box-shadow: 0 4px 16px rgba(0, 122, 255, 0.3) !important;
|
||||
transition: all 0.3s ease !important;
|
||||
|
||||
@@ -48,7 +48,10 @@
|
||||
<view class="card-header">
|
||||
<!-- 第一行:标题和卫生等级 -->
|
||||
<view class="header-row">
|
||||
<view class="check-title">{{ item.checkTitle || '-' }}</view>
|
||||
<view class="check-title">
|
||||
<text class="title-index">{{ index + 1 }}.</text>
|
||||
{{ item.checkTitle || '-' }}
|
||||
</view>
|
||||
<view class="grade-section">
|
||||
<u-tag
|
||||
:text="$getDictLabel(xg_dorm_hygiene_grade, item.hygieneGrade)"
|
||||
@@ -151,22 +154,37 @@
|
||||
<!-- 操作按钮 -->
|
||||
<view class="card-actions">
|
||||
<!-- 查看按钮 -->
|
||||
<view class="action-btn primary-btn" @click="handleView(item)">
|
||||
<u-icon name="eye" size="16" color="#19be6b"></u-icon>
|
||||
<text class="btn-text">查看</text>
|
||||
</view>
|
||||
<u-button
|
||||
type="info"
|
||||
size="small"
|
||||
class="action-btn"
|
||||
@click="handleView(item)"
|
||||
>
|
||||
<u-icon name="eye" size="16" color="#323233"></u-icon>
|
||||
<text>查看</text>
|
||||
</u-button>
|
||||
|
||||
<!-- 修改按钮 -->
|
||||
<view class="action-btn info-btn" @click="handleUpdate(item)">
|
||||
<u-icon name="edit-pen" size="16" color="#3c9cff"></u-icon>
|
||||
<u-button
|
||||
type="primary"
|
||||
size="small"
|
||||
class="action-btn"
|
||||
@click="handleUpdate(item)"
|
||||
>
|
||||
<u-icon name="edit-pen" size="16" color="#ffffff"></u-icon>
|
||||
<text class="btn-text">修改</text>
|
||||
</view>
|
||||
</u-button>
|
||||
|
||||
<!-- 删除按钮 -->
|
||||
<view class="action-btn danger-btn" @click="handleDelete(item)">
|
||||
<u-icon name="trash" size="16" color="#f56c6c"></u-icon>
|
||||
<u-button
|
||||
type="error"
|
||||
size="small"
|
||||
class="action-btn"
|
||||
@click="handleDelete(item)"
|
||||
>
|
||||
<u-icon name="trash" size="16" color="#ffffff"></u-icon>
|
||||
<text class="btn-text">删除</text>
|
||||
</view>
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -608,6 +626,12 @@ export default {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.title-index {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.check-level {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
@@ -733,37 +757,26 @@ export default {
|
||||
|
||||
.action-btn {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 16rpx 20rpx;
|
||||
border-radius: 12rpx;
|
||||
min-width: 120rpx;
|
||||
transition: all 0.3s ease;
|
||||
background-color: transparent;
|
||||
border-radius: 20rpx;
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
opacity: 0.8;
|
||||
::v-deep .u-button {
|
||||
border-radius: 20rpx;
|
||||
padding: 16rpx 20rpx;
|
||||
min-width: 120rpx;
|
||||
height: auto;
|
||||
line-height: 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 24rpx;
|
||||
margin-top: 8rpx;
|
||||
margin-left: 8rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.primary-btn .btn-text {
|
||||
color: #19be6b;
|
||||
}
|
||||
|
||||
.info-btn .btn-text {
|
||||
color: #3c9cff;
|
||||
}
|
||||
|
||||
.danger-btn .btn-text {
|
||||
color: #f56c6c;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
// 分页容器
|
||||
|
||||
@@ -60,7 +60,10 @@
|
||||
<view class="card-header">
|
||||
<!-- 第一行:标题和卫生等级 -->
|
||||
<view class="header-row">
|
||||
<view class="check-title">{{ item.checkTitle || '-' }}</view>
|
||||
<view class="check-title">
|
||||
<text class="check-index">{{ index + 1 }}.</text>
|
||||
<text>{{ item.checkTitle || '-' }}</text>
|
||||
</view>
|
||||
<view class="grade-section">
|
||||
<u-tag
|
||||
:text="$getDictLabel(xg_dorm_hygiene_grade, item.hygieneGrade)"
|
||||
@@ -189,8 +192,8 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分页加载(已废弃,因为接口返回完整列表) -->
|
||||
<!-- <view v-if="checkList.length > 0" class="pagination-container">
|
||||
<!-- 分页加载 -->
|
||||
<view v-if="checkList.length > 0" class="pagination-container">
|
||||
<u-loadmore
|
||||
:status="loadStatus"
|
||||
@loadmore="loadMore"
|
||||
@@ -198,7 +201,7 @@
|
||||
loading-text="正在加载..."
|
||||
no-more-text="没有更多数据了"
|
||||
/>
|
||||
</view> -->
|
||||
</view>
|
||||
|
||||
<!-- 悬浮添加按钮 -->
|
||||
<!-- <view class="fab-button" @click="handleAdd">
|
||||
@@ -227,7 +230,6 @@ export default {
|
||||
|
||||
// 检查记录列表
|
||||
checkList: [],
|
||||
originalCheckList: [], // 原始数据,用于前端过滤
|
||||
total: 0,
|
||||
|
||||
// 分页加载状态
|
||||
@@ -260,11 +262,11 @@ export default {
|
||||
uni.$off('refreshCheckList', this.handleRefreshEvent);
|
||||
},
|
||||
|
||||
// 页面滚动到底部事件(已废弃,因为接口返回完整列表)
|
||||
// onReachBottom() {
|
||||
// console.log('🔄 触发滚动到底部事件,开始加载更多...');
|
||||
// this.loadMore();
|
||||
// },
|
||||
// 页面滚动到底部事件
|
||||
onReachBottom() {
|
||||
console.log('🔄 触发滚动到底部事件,开始加载更多...');
|
||||
this.loadMore();
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 处理刷新事件
|
||||
@@ -313,7 +315,11 @@ export default {
|
||||
this.loading = true;
|
||||
this.queryParams.pageNum = 1;
|
||||
this.checkList = [];
|
||||
this.loadStatus = 'loadmore';
|
||||
} else {
|
||||
if (this.loadStatus === 'loading' || this.loadStatus === 'nomore') {
|
||||
return;
|
||||
}
|
||||
this.loadStatus = 'loading';
|
||||
}
|
||||
|
||||
@@ -331,35 +337,50 @@ export default {
|
||||
console.log('📋 开始加载当前学生的宿舍卫生检查记录...', {
|
||||
学生ID: currentUser.userId,
|
||||
查询参数: this.queryParams,
|
||||
是否刷新: isRefresh,
|
||||
});
|
||||
|
||||
// 调用根据学生ID查询卫生检查记录的接口
|
||||
// 构建请求参数
|
||||
const params = {
|
||||
pageNum: this.queryParams.pageNum,
|
||||
pageSize: this.queryParams.pageSize,
|
||||
};
|
||||
|
||||
// 添加搜索条件
|
||||
if (this.queryParams.checkTitle && this.queryParams.checkTitle.trim()) {
|
||||
params.checkTitle = this.queryParams.checkTitle.trim();
|
||||
}
|
||||
|
||||
// 调用分页接口
|
||||
const res = await request({
|
||||
url: `/dormHygieneCheck/dormHygieneCheck/student/${currentUser.userId}`,
|
||||
url: `/dormHygieneCheck/dormHygieneCheck/student/${currentUser.userId}/page`,
|
||||
method: 'GET',
|
||||
params: params,
|
||||
});
|
||||
|
||||
if (res && res.code === 200) {
|
||||
const newData = res.data || [];
|
||||
const newData = res.rows || [];
|
||||
const total = res.total || 0;
|
||||
|
||||
if (isRefresh) {
|
||||
this.originalCheckList = newData; // 保存原始数据
|
||||
this.checkList = newData;
|
||||
} else {
|
||||
this.originalCheckList = [...this.originalCheckList, ...newData];
|
||||
this.checkList = [...this.checkList, ...newData];
|
||||
}
|
||||
|
||||
this.total = newData.length;
|
||||
this.total = total;
|
||||
|
||||
// 由于getHygieneChecksByStudentId返回的是完整列表,不需要分页加载
|
||||
this.loadStatus = 'nomore';
|
||||
// 判断是否还有更多数据
|
||||
const hasMore = this.queryParams.pageNum * this.queryParams.pageSize < total;
|
||||
this.loadStatus = hasMore ? 'loadmore' : 'nomore';
|
||||
|
||||
console.log('✅ 学生宿舍卫生检查记录加载成功:', {
|
||||
学生ID: currentUser.userId,
|
||||
当前页码: this.queryParams.pageNum,
|
||||
本次加载: newData.length,
|
||||
总数: this.total,
|
||||
总数: total,
|
||||
列表长度: this.checkList.length,
|
||||
是否还有更多: hasMore,
|
||||
});
|
||||
} else {
|
||||
console.warn('获取学生宿舍卫生检查记录失败:', res?.msg);
|
||||
@@ -368,9 +389,7 @@ export default {
|
||||
icon: 'none',
|
||||
});
|
||||
|
||||
if (!isRefresh) {
|
||||
this.loadStatus = 'loadmore';
|
||||
}
|
||||
this.loadStatus = isRefresh ? 'loadmore' : 'loadmore';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ 加载学生宿舍卫生检查记录失败:', error);
|
||||
@@ -379,9 +398,7 @@ export default {
|
||||
icon: 'none',
|
||||
});
|
||||
|
||||
if (!isRefresh) {
|
||||
this.loadStatus = 'loadmore';
|
||||
}
|
||||
this.loadStatus = isRefresh ? 'loadmore' : 'loadmore';
|
||||
} finally {
|
||||
if (isRefresh) {
|
||||
this.loading = false;
|
||||
@@ -389,51 +406,35 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 加载更多(已废弃,因为getHygieneChecksByStudentId返回完整列表)
|
||||
// 加载更多
|
||||
async loadMore() {
|
||||
console.log('📄 当前接口返回完整列表,无需分页加载');
|
||||
// 由于getHygieneChecksByStudentId返回的是完整列表,不需要分页加载
|
||||
this.loadStatus = 'nomore';
|
||||
console.log('📄 触发加载更多,当前状态:', this.loadStatus);
|
||||
|
||||
if (this.loadStatus !== 'loadmore') {
|
||||
console.log('📄 当前状态不允许加载更多:', this.loadStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
// 增加页码,加载下一页数据
|
||||
this.queryParams.pageNum += 1;
|
||||
console.log('📄 加载第', this.queryParams.pageNum, '页数据');
|
||||
|
||||
await this.loadCheckList(false);
|
||||
},
|
||||
|
||||
// 搜索操作(前端过滤)
|
||||
// 搜索操作(后端过滤)
|
||||
handleQuery() {
|
||||
console.log('🔍 执行前端搜索过滤:', this.queryParams);
|
||||
// 由于接口返回完整列表,在前端进行过滤
|
||||
this.filterCheckList();
|
||||
console.log('🔍 执行搜索查询:', this.queryParams);
|
||||
// 重新从第一页开始搜索
|
||||
this.loadCheckList(true);
|
||||
},
|
||||
|
||||
// 重置搜索
|
||||
resetQuery() {
|
||||
this.queryParams.checkTitle = '';
|
||||
this.filterCheckList();
|
||||
},
|
||||
|
||||
// 前端过滤检查记录列表
|
||||
filterCheckList() {
|
||||
if (!this.originalCheckList || this.originalCheckList.length === 0) {
|
||||
this.checkList = [];
|
||||
return;
|
||||
}
|
||||
|
||||
let filteredList = [...this.originalCheckList];
|
||||
|
||||
// 按检查标题过滤
|
||||
if (this.queryParams.checkTitle && this.queryParams.checkTitle.trim()) {
|
||||
const searchTerm = this.queryParams.checkTitle.trim().toLowerCase();
|
||||
filteredList = filteredList.filter(
|
||||
(item) => item.checkTitle && item.checkTitle.toLowerCase().includes(searchTerm),
|
||||
);
|
||||
}
|
||||
|
||||
this.checkList = filteredList;
|
||||
this.total = filteredList.length;
|
||||
|
||||
console.log('✅ 前端过滤完成:', {
|
||||
原始数据: this.originalCheckList.length,
|
||||
过滤后: this.checkList.length,
|
||||
搜索条件: this.queryParams.checkTitle,
|
||||
});
|
||||
console.log('🔄 重置搜索条件');
|
||||
// 重新加载数据
|
||||
this.loadCheckList(true);
|
||||
},
|
||||
|
||||
// 获取检查级别类型
|
||||
@@ -655,6 +656,14 @@ export default {
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.check-index {
|
||||
color: #000000;
|
||||
font-weight: bold;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
.check-level {
|
||||
@@ -815,10 +824,10 @@ export default {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
// 分页容器(已废弃)
|
||||
// .pagination-container {
|
||||
// padding: 20rpx;
|
||||
// }
|
||||
// 分页容器
|
||||
.pagination-container {
|
||||
padding: 20rpx;
|
||||
}
|
||||
|
||||
// 搜索按钮样式
|
||||
::v-deep .search-btn {
|
||||
|
||||
+206
-119
@@ -17,101 +17,126 @@
|
||||
<u-empty text="暂无可申请的考核计划" mode="data"></u-empty>
|
||||
</view>
|
||||
|
||||
<!-- 考核计划列表 -->
|
||||
<view v-else class="schedule-list">
|
||||
<view
|
||||
v-for="(item, index) in assessmentScheduleList"
|
||||
:key="item.id || index"
|
||||
class="schedule-card"
|
||||
>
|
||||
<!-- 卡片头部 -->
|
||||
<view class="card-header">
|
||||
<view class="schedule-info">
|
||||
<text class="schedule-name">{{ item.scheduleName || '暂无' }}</text>
|
||||
<view class="schedule-meta">
|
||||
<text class="academic-info">
|
||||
{{ item.academicYear || '暂无' }}
|
||||
{{ getSemesterText(item.semester) }}
|
||||
<!-- 考核计划列表 - 滚动加载 -->
|
||||
<scroll-view
|
||||
v-else
|
||||
class="schedule-scroll-view"
|
||||
scroll-y
|
||||
@scrolltolower="handleScrollToLower"
|
||||
:lower-threshold="100"
|
||||
>
|
||||
<view class="schedule-list">
|
||||
<view
|
||||
v-for="(item, index) in assessmentScheduleList"
|
||||
:key="item.id || index"
|
||||
class="schedule-card"
|
||||
>
|
||||
<!-- 卡片头部 -->
|
||||
<view class="card-header">
|
||||
<view class="schedule-info">
|
||||
<view class="schedule-name-wrapper">
|
||||
<text class="schedule-index">{{ index + 1 }}.</text>
|
||||
<text class="schedule-name">
|
||||
{{ item.scheduleName || '暂无' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="schedule-meta">
|
||||
<text class="academic-info">
|
||||
{{ item.academicYear || '暂无' }}
|
||||
{{ getSemesterText(item.semester) }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="status-info">
|
||||
<u-tag
|
||||
:text="getStatusText(item.status)"
|
||||
:type="getStatusTagType(item.status)"
|
||||
size="mini"
|
||||
></u-tag>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片内容 -->
|
||||
<view class="card-content">
|
||||
<view class="schedule-description">
|
||||
<text class="description-text">
|
||||
{{ item.description || '暂无说明' }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="time-info">
|
||||
<view class="time-item">
|
||||
<text class="time-label">开始时间:</text>
|
||||
<text class="time-value">{{ formatTime(item.startTime) }}</text>
|
||||
</view>
|
||||
<view class="time-item">
|
||||
<text class="time-label">结束时间:</text>
|
||||
<text class="time-value">{{ formatTime(item.endTime) }}</text>
|
||||
</view>
|
||||
<view class="time-item">
|
||||
<text class="time-label">数据统计开始时间:</text>
|
||||
<text class="time-value">
|
||||
{{ formatTime(item.dataStartTime) }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="time-item">
|
||||
<text class="time-label">数据统计结束时间:</text>
|
||||
<text class="time-value">
|
||||
{{ formatTime(item.dataEndTime) }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="plan-info">
|
||||
<text class="plan-name">
|
||||
考核方案: {{ item.planName || '暂无' }}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="status-info">
|
||||
<u-tag
|
||||
:text="getStatusText(item.status)"
|
||||
:type="getStatusTagType(item.status)"
|
||||
size="mini"
|
||||
></u-tag>
|
||||
|
||||
<!-- 卡片操作按钮 -->
|
||||
<view class="card-actions">
|
||||
<u-button
|
||||
type="info"
|
||||
size="small"
|
||||
icon="eye"
|
||||
shape="round"
|
||||
@click="handleViewSchedule(item)"
|
||||
>
|
||||
查看计划
|
||||
</u-button>
|
||||
<u-button
|
||||
v-if="canSubmitApplication(item)"
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="edit-pen"
|
||||
shape="round"
|
||||
@click="handleSubmitApplication(item)"
|
||||
>
|
||||
提交申请
|
||||
</u-button>
|
||||
<u-button
|
||||
type="warning"
|
||||
size="small"
|
||||
icon="file-text"
|
||||
shape="round"
|
||||
@click="handleViewApplication(item)"
|
||||
>
|
||||
查看申请
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片内容 -->
|
||||
<view class="card-content">
|
||||
<view class="schedule-description">
|
||||
<text class="description-text">
|
||||
{{ item.description || '暂无说明' }}
|
||||
</text>
|
||||
<!-- 加载更多状态提示 -->
|
||||
<view v-if="assessmentScheduleList.length > 0" class="load-more">
|
||||
<view v-if="loadingMore" class="loading-more">
|
||||
<u-loading-icon mode="circle" size="16"></u-loading-icon>
|
||||
<text class="loading-text">正在加载更多...</text>
|
||||
</view>
|
||||
<view class="time-info">
|
||||
<view class="time-item">
|
||||
<text class="time-label">开始时间:</text>
|
||||
<text class="time-value">{{ formatTime(item.startTime) }}</text>
|
||||
</view>
|
||||
<view class="time-item">
|
||||
<text class="time-label">结束时间:</text>
|
||||
<text class="time-value">{{ formatTime(item.endTime) }}</text>
|
||||
</view>
|
||||
<view class="time-item">
|
||||
<text class="time-label">数据统计开始时间:</text>
|
||||
<text class="time-value">{{ formatTime(item.dataStartTime) }}</text>
|
||||
</view>
|
||||
<view class="time-item">
|
||||
<text class="time-label">数据统计结束时间:</text>
|
||||
<text class="time-value">{{ formatTime(item.dataEndTime) }}</text>
|
||||
</view>
|
||||
<view v-else class="load-complete-text">
|
||||
{{ getNoMoreText() }}
|
||||
</view>
|
||||
<view class="plan-info">
|
||||
<text class="plan-name">考核方案: {{ item.planName || '暂无' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 卡片操作按钮 -->
|
||||
<view class="card-actions">
|
||||
<u-button
|
||||
type="info"
|
||||
size="small"
|
||||
icon="eye"
|
||||
shape="round"
|
||||
@click="handleViewSchedule(item)"
|
||||
>
|
||||
查看计划
|
||||
</u-button>
|
||||
<u-button
|
||||
v-if="canSubmitApplication(item)"
|
||||
type="primary"
|
||||
size="small"
|
||||
icon="edit-pen"
|
||||
shape="round"
|
||||
@click="handleSubmitApplication(item)"
|
||||
>
|
||||
提交申请
|
||||
</u-button>
|
||||
<u-button
|
||||
type="warning"
|
||||
size="small"
|
||||
icon="file-text"
|
||||
shape="round"
|
||||
@click="handleViewApplication(item)"
|
||||
>
|
||||
查看申请
|
||||
</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分页加载 -->
|
||||
<view v-if="hasMore && assessmentScheduleList.length > 0" class="load-more">
|
||||
<u-loadmore :status="loadStatus" @loadmore="loadMore"></u-loadmore>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<!-- 查看计划详情弹窗 -->
|
||||
@@ -231,6 +256,7 @@ export default {
|
||||
total: 0,
|
||||
hasMore: true,
|
||||
loadStatus: 'loadmore',
|
||||
loadingMore: false,
|
||||
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
@@ -276,9 +302,14 @@ export default {
|
||||
},
|
||||
|
||||
// 获取考核计划列表
|
||||
async getList(loadMore = false) {
|
||||
if (!loadMore) {
|
||||
async getList(isLoadMore = false) {
|
||||
if (isLoadMore) {
|
||||
this.loadingMore = true;
|
||||
} else {
|
||||
this.loading = true;
|
||||
// 重置分页参数
|
||||
this.queryParams.pageNum = 1;
|
||||
this.assessmentScheduleList = [];
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -290,38 +321,49 @@ export default {
|
||||
|
||||
if (res && res.code === 200) {
|
||||
const rows = res.rows || [];
|
||||
const total = res.total || 0;
|
||||
|
||||
if (loadMore) {
|
||||
this.assessmentScheduleList = this.assessmentScheduleList.concat(rows);
|
||||
if (isLoadMore) {
|
||||
// 加载更多时追加数据
|
||||
this.assessmentScheduleList = [...this.assessmentScheduleList, ...rows];
|
||||
} else {
|
||||
// 首次加载时直接设置数据
|
||||
this.assessmentScheduleList = rows;
|
||||
}
|
||||
|
||||
this.total = res.total || 0;
|
||||
this.total = total;
|
||||
|
||||
if (rows.length < this.queryParams.pageSize) {
|
||||
this.hasMore = false;
|
||||
this.loadStatus = 'nomore';
|
||||
} else {
|
||||
this.hasMore = true;
|
||||
this.loadStatus = 'loadmore';
|
||||
}
|
||||
// 判断是否还有更多数据
|
||||
const currentTotal = this.assessmentScheduleList.length;
|
||||
this.hasMore = currentTotal < total;
|
||||
this.loadStatus = this.hasMore ? 'loadmore' : 'nomore';
|
||||
|
||||
console.log('获取考核计划数据成功:', {
|
||||
当前页: this.queryParams.pageNum,
|
||||
返回条数: rows.length,
|
||||
累计条数: currentTotal,
|
||||
总数量: total,
|
||||
是否还有更多: this.hasMore,
|
||||
});
|
||||
} else {
|
||||
this.handleRequestError(res?.msg || '获取数据失败', loadMore);
|
||||
this.handleRequestError(res?.msg || '获取数据失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取数据失败:', error);
|
||||
this.handleRequestError('获取数据失败', loadMore);
|
||||
this.handleRequestError('获取数据失败');
|
||||
} finally {
|
||||
if (!loadMore) {
|
||||
if (isLoadMore) {
|
||||
this.loadingMore = false;
|
||||
} else {
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 错误处理
|
||||
handleRequestError(message, isLoadMore = false) {
|
||||
if (!isLoadMore) {
|
||||
handleRequestError(message) {
|
||||
if (this.queryParams.pageNum === 1) {
|
||||
// 首次加载失败,清空数据
|
||||
this.assessmentScheduleList = [];
|
||||
this.total = 0;
|
||||
}
|
||||
@@ -334,22 +376,22 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
// 加载更多
|
||||
async loadMore() {
|
||||
if (!this.hasMore || this.loadStatus !== 'loadmore') {
|
||||
// 滚动到底部触发加载更多
|
||||
async handleScrollToLower() {
|
||||
if (!this.hasMore || this.loadingMore || this.loading) {
|
||||
console.log('无需加载更多数据:', {
|
||||
hasMore: this.hasMore,
|
||||
loadingMore: this.loadingMore,
|
||||
loading: this.loading,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadStatus = 'loading';
|
||||
this.queryParams.pageNum += 1;
|
||||
console.log('滚动到底部,开始加载更多数据');
|
||||
|
||||
try {
|
||||
await this.getList(true);
|
||||
} catch (error) {
|
||||
console.error('加载更多数据失败:', error);
|
||||
this.queryParams.pageNum -= 1;
|
||||
this.loadStatus = 'loadmore';
|
||||
}
|
||||
// 页码加1,加载下一页
|
||||
this.queryParams.pageNum += 1;
|
||||
await this.getList(true);
|
||||
},
|
||||
|
||||
// 查看考核计划信息
|
||||
@@ -466,9 +508,6 @@ export default {
|
||||
|
||||
// 提交成功回调
|
||||
handleSuccess() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.hasMore = true;
|
||||
this.loadStatus = 'loadmore';
|
||||
this.getList();
|
||||
},
|
||||
|
||||
@@ -514,6 +553,16 @@ export default {
|
||||
};
|
||||
return getDictTagType(this.assessmentScheduleStatus, status, fallbackMap);
|
||||
},
|
||||
|
||||
// 获取没有更多数据文本
|
||||
getNoMoreText() {
|
||||
if (this.hasMore) {
|
||||
return `已加载${this.assessmentScheduleList.length}条,共${this.total}条`;
|
||||
} else if (this.total > 0) {
|
||||
return `已全部加载完毕,共${this.total}条考核计划`;
|
||||
}
|
||||
return '暂无数据';
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -526,6 +575,11 @@ export default {
|
||||
|
||||
.content-wrapper {
|
||||
padding: 20rpx;
|
||||
height: calc(100vh - 88rpx); /* 减去导航栏高度 */
|
||||
}
|
||||
|
||||
.schedule-scroll-view {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.empty-container {
|
||||
@@ -558,12 +612,25 @@ export default {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.schedule-name-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.schedule-index {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
margin-right: 8rpx;
|
||||
min-width: 40rpx;
|
||||
}
|
||||
|
||||
.schedule-name {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
display: block;
|
||||
margin-bottom: 12rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.schedule-meta {
|
||||
@@ -640,6 +707,26 @@ export default {
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.loading-more {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20rpx 0;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 24rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.load-complete-text {
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
color: #909399;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
/* 弹窗样式 */
|
||||
.schedule-detail-popup {
|
||||
background-color: #fff;
|
||||
|
||||
@@ -26,7 +26,10 @@
|
||||
>
|
||||
<!-- 卡片头部 -->
|
||||
<view class="card-header">
|
||||
<view class="record-title">{{ item.disciplineTypeName || '-' }}</view>
|
||||
<view class="record-title">
|
||||
<text class="record-number">{{ index + 1 }}.</text>
|
||||
{{ item.disciplineTypeName || '-' }}
|
||||
</view>
|
||||
<view class="record-status">
|
||||
<u-tag
|
||||
:text="getDictLabel(studentDisciplineStatus, item.status)"
|
||||
@@ -114,9 +117,15 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 滚动加载提示 -->
|
||||
<view v-if="hasMore && disciplineRecordList.length > 0" class="load-more">
|
||||
<u-loadmore :status="loadStatus" @loadmore="loadMore"></u-loadmore>
|
||||
<!-- 底部加载状态提示 -->
|
||||
<view v-if="disciplineRecordList.length > 0" class="load-status">
|
||||
<view v-if="loadingMore" class="loading-tip">
|
||||
<u-loading-icon></u-loading-icon>
|
||||
<text class="loading-text">加载中...</text>
|
||||
</view>
|
||||
<view v-else-if="!hasMore" class="no-more-tip">
|
||||
<text>已加载全部数据</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -143,10 +152,10 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
loadingMore: false, // 加载更多状态
|
||||
disciplineRecordList: [],
|
||||
total: 0,
|
||||
hasMore: true,
|
||||
loadStatus: 'loadmore',
|
||||
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
@@ -172,6 +181,16 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 滚动到底部时自动加载更多
|
||||
onReachBottom() {
|
||||
this.loadMore();
|
||||
},
|
||||
|
||||
// 下拉刷新
|
||||
onPullDownRefresh() {
|
||||
this.handleRefresh();
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 返回上一页
|
||||
goBack() {
|
||||
@@ -205,6 +224,8 @@ export default {
|
||||
async getList(loadMore = false) {
|
||||
if (!loadMore) {
|
||||
this.loading = true;
|
||||
} else {
|
||||
this.loadingMore = true;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -230,10 +251,8 @@ export default {
|
||||
// 判断是否还有更多数据
|
||||
if (rows.length < this.queryParams.pageSize) {
|
||||
this.hasMore = false;
|
||||
this.loadStatus = 'nomore';
|
||||
} else {
|
||||
this.hasMore = true;
|
||||
this.loadStatus = 'loadmore';
|
||||
}
|
||||
} else {
|
||||
if (!loadMore) {
|
||||
@@ -241,7 +260,6 @@ export default {
|
||||
this.total = 0;
|
||||
}
|
||||
this.hasMore = false;
|
||||
this.loadStatus = 'nomore';
|
||||
|
||||
uni.showToast({
|
||||
title: res?.msg || '获取数据失败',
|
||||
@@ -256,7 +274,6 @@ export default {
|
||||
this.total = 0;
|
||||
}
|
||||
this.hasMore = false;
|
||||
this.loadStatus = 'nomore';
|
||||
|
||||
uni.showToast({
|
||||
title: '获取数据失败',
|
||||
@@ -265,17 +282,19 @@ export default {
|
||||
} finally {
|
||||
if (!loadMore) {
|
||||
this.loading = false;
|
||||
} else {
|
||||
this.loadingMore = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 加载更多数据
|
||||
async loadMore() {
|
||||
if (!this.hasMore || this.loadStatus !== 'loadmore') {
|
||||
// 防止重复加载
|
||||
if (!this.hasMore || this.loadingMore || this.loading) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadStatus = 'loading';
|
||||
this.queryParams.pageNum += 1;
|
||||
|
||||
try {
|
||||
@@ -283,7 +302,25 @@ export default {
|
||||
} catch (error) {
|
||||
console.error('加载更多数据失败:', error);
|
||||
this.queryParams.pageNum -= 1; // 回退页码
|
||||
this.loadStatus = 'loadmore';
|
||||
|
||||
uni.showToast({
|
||||
title: '加载失败,请重试',
|
||||
icon: 'none',
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 下拉刷新处理
|
||||
async handleRefresh() {
|
||||
this.queryParams.pageNum = 1;
|
||||
this.hasMore = true;
|
||||
|
||||
try {
|
||||
await this.getList();
|
||||
} catch (error) {
|
||||
console.error('刷新数据失败:', error);
|
||||
} finally {
|
||||
uni.stopPullDownRefresh();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -318,7 +355,6 @@ export default {
|
||||
// 重新加载列表
|
||||
this.queryParams.pageNum = 1;
|
||||
this.hasMore = true;
|
||||
this.loadStatus = 'loadmore';
|
||||
this.getList();
|
||||
},
|
||||
|
||||
@@ -354,6 +390,7 @@ export default {
|
||||
|
||||
.content-wrapper {
|
||||
padding: 20rpx;
|
||||
padding-bottom: 40rpx; // 增加底部间距
|
||||
}
|
||||
|
||||
.empty-container {
|
||||
@@ -386,6 +423,15 @@ export default {
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.record-number {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
margin-right: 8rpx;
|
||||
min-width: 40rpx;
|
||||
}
|
||||
|
||||
.record-status {
|
||||
@@ -434,12 +480,42 @@ export default {
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.load-more {
|
||||
// 底部加载状态样式
|
||||
.load-status {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 40rpx 20rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.loading-tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16rpx;
|
||||
|
||||
.loading-text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.no-more-tip {
|
||||
text {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
// uview组件样式调整
|
||||
::v-deep .u-tag {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
// 加载图标样式调整
|
||||
::v-deep .u-loading-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
</style>
|
||||
Reference in new issue
Block a user