From 719f69fe11bda0d7dcc2d2df251cdab31b88b140 Mon Sep 17 00:00:00 2001 From: zlwu <704541015@qq.com> Date: Wed, 13 Aug 2025 15:02:38 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/nohup.out | 117 ------ backend/quota_diagnosis_results.txt | 37 -- .../dromara/xg/mapper/XgDormBedMapper.java | 18 +- .../impl/XgDormBedStudentServiceImpl.java | 23 +- web/src/api/xg/xgss/dormBed/index.ts | 1 + .../components/CollegeAllocationDialog.vue | 314 ++++++++++++--- web/src/views/xg/xgss/xsssfpejxy/index.vue | 381 +++++++++++++----- 7 files changed, 579 insertions(+), 312 deletions(-) delete mode 100644 backend/nohup.out delete mode 100644 backend/quota_diagnosis_results.txt diff --git a/backend/nohup.out b/backend/nohup.out deleted file mode 100644 index b892ecc2..00000000 --- a/backend/nohup.out +++ /dev/null @@ -1,117 +0,0 @@ -Application Version: 5.4.0 -Spring Boot Version: 3.4.6 -__________ _____.___.__ ____ ____ __________.__ -\______ \__ __ ____\__ | |__| \ \ / /_ __ ____ \______ \ | __ __ ______ - | _/ | \/ _ \/ | | | ______ \ Y / | \_/ __ \ ______ | ___/ | | | \/ ___/ - | | \ | ( <_> )____ | | /_____/ \ /| | /\ ___/ /_____/ | | | |_| | /\___ \ - |____|_ /____/ \____// ______|__| \___/ |____/ \___ > |____| |____/____//____ > - \/ \/ \/ \/ - -2025-07-26 13:02:51 [background-preinit] INFO  o.h.validator.internal.util.Version - - HV000001: Hibernate Validator 8.0.2.Final -2025-07-26 13:02:51 [restartedMain] INFO  org.dromara.DromaraApplication - - Starting DromaraApplication using Java 17.0.9 with PID 89083 (/Users/wuzhen/Documents/myworkspace/myproject/hzdyjspro/all-project/backend/ruoyi-admin/target/classes started by wuzhen in /Users/wuzhen/Documents/myworkspace/myproject/hzdyjspro/all-project/backend/ruoyi-admin) -2025-07-26 13:02:51 [restartedMain] INFO  org.dromara.DromaraApplication - - The following 1 profile is active: "dev" -2025-07-26 13:02:55 [restartedMain] DEBUG o.apache.ibatis.logging.LogFactory - - Logging initialized using 'class org.apache.ibatis.logging.slf4j.Slf4jImpl' adapter. -2025-07-26 13:02:57 [restartedMain] INFO  io.undertow.servlet - - Initializing Spring embedded WebApplicationContext -2025-07-26 13:02:58 [restartedMain] INFO  c.b.d.d.DynamicRoutingDataSource - - dynamic-datasource detect P6SPY plugin and enabled it -2025-07-26 13:02:58 [restartedMain] INFO  com.zaxxer.hikari.HikariDataSource - - master - Starting... -2025-07-26 13:02:58 [restartedMain] INFO  com.zaxxer.hikari.pool.HikariPool - - master - Added connection com.mysql.cj.jdbc.ConnectionImpl@47691398 -2025-07-26 13:02:58 [restartedMain] INFO  com.zaxxer.hikari.HikariDataSource - - master - Start completed. -2025-07-26 13:02:58 [restartedMain] INFO  c.b.d.d.DynamicRoutingDataSource - - dynamic-datasource - add a datasource named [master] success -2025-07-26 13:02:58 [restartedMain] INFO  c.b.d.d.DynamicRoutingDataSource - - dynamic-datasource initial loaded [1] datasource,primary datasource named [master] -2025-07-26 13:03:00 [restartedMain] WARN  c.b.m.c.injector.DefaultSqlInjector - - class org.dromara.xg.domain.XgSysDept ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method. -2025-07-26 13:03:01 [restartedMain] ERROR c.b.m.core.MybatisConfiguration - - mapper[org.dromara.zsjy.mapper.ZsjyEnrollmentProcessMapper.countByPaymentStatus] is ignored, because it exists, maybe from xml file -2025-07-26 13:03:01 [restartedMain] ERROR c.b.m.core.MybatisConfiguration - - mapper[org.dromara.zsjy.mapper.ZsjyEnrollmentProcessMapper.countByAdmissionStatus] is ignored, because it exists, maybe from xml file -2025-07-26 13:03:01 [restartedMain] ERROR c.b.m.core.MybatisConfiguration - - mapper[org.dromara.zsjy.mapper.ZsjyEnrollmentProcessMapper.selectByEnrollmentId] is ignored, because it exists, maybe from xml file - _ _ |_ _ _|_. ___ _ | _ -| | |\/|_)(_| | |_\ |_)||_|_\ - / | - 3.5.12 -2025-07-26 13:03:02 [restartedMain] INFO  o.d.common.redis.config.RedisConfig - - 初始化 redis 配置 -2025-07-26 13:03:02 [restartedMain] INFO  org.redisson.Version - - Redisson 3.45.1 -2025-07-26 13:03:02 [redisson-netty-1-6] INFO  o.r.connection.ConnectionsHolder - - 1 connections initialized for 47.99.164.66/47.99.164.66:16379 -2025-07-26 13:03:03 [redisson-netty-1-3] INFO  o.r.connection.ConnectionsHolder - - 8 connections initialized for 47.99.164.66/47.99.164.66:16379 -2025-07-26 13:03:06 [restartedMain] INFO  c.a.s.c.j.c.e.b.AbstractScriptExecutor$SnailFileUtils - - [FileUtils] [workspace] use user.home as workspace: /Users/wuzhen/snailJob/worker -2025-07-26 13:03:06 [restartedMain] INFO  c.a.s.c.j.c.e.b.AbstractScriptExecutor$SnailFileUtils - - [FileUtils] [workspace] use user.home as workspace: /Users/wuzhen/snailJob/worker -2025-07-26 13:03:06 [restartedMain] INFO  c.a.s.c.j.c.e.b.AbstractScriptExecutor$SnailFileUtils - - [FileUtils] [workspace] use user.home as workspace: /Users/wuzhen/snailJob/worker -2025-07-26 13:03:07 [restartedMain] INFO  o.d.common.json.config.JacksonConfig - - 初始化 jackson 配置 -____ ____ ___ ____ _ _ ____ _ _ -[__ |__| __ | | | |_/ |___ |\ | -___] | | | |__| | \_ |___ | \| -https://sa-token.cc (v1.42.0) -2025-07-26 13:03:08 [restartedMain] INFO  c.b.m.e.s.MybatisPlusApplicationContextAware - - Register ApplicationContext instances org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@1d95e68a - - ▄ ▄ ▄▄▄▄▄▄ ▄ - █ █ █ ▄▄▄ ▄ ▄▄ ▄▄▄▄▄ █ █ ▄▄▄ ▄ ▄ - ▀ █▀█ █ ▀ █ █▀ ▀ █ █ █ ▄▄▄▄▄ █▄▄▄▄▄ █ █▀ ▀█ ▀▄ ▄ ▄▀ - ██ ██▀ ▄▀▀▀█ █ █ █ █ █ █ █ █ █▄█▄█ - █ █ ▀▄▄▀█ █ █ █ █ █ █▄▄ ▀█▄█▀ █ █ - - :: Warm-Flow :: (v1.7.3) - -2025-07-26 13:03:24 [restartedMain] INFO  o.d.w.p.modes.sb.config.BeanConfig - - 【warm-flow】,加载完成 -2025-07-26 13:03:26 [restartedMain] WARN  o.s.b.d.a.OptionalLiveReloadServer - - Unable to start LiveReload server -2025-07-26 13:03:27 [restartedMain] INFO  io.undertow - - starting server: Undertow - 2.3.18.Final -2025-07-26 13:03:27 [restartedMain] INFO  org.xnio - - XNIO version 3.8.16.Final -2025-07-26 13:03:28 [restartedMain] INFO  org.xnio.nio - - XNIO NIO Implementation Version 3.8.16.Final -2025-07-26 13:03:28 [restartedMain] INFO  org.jboss.threads - - JBoss Threads version 3.5.0.Final -2025-07-26 13:03:28 [restartedMain] INFO  io.undertow - - stopping server: Undertow - 2.3.18.Final -2025-07-26 13:03:28 [restartedMain] WARN  o.s.b.w.s.c.AnnotationConfigServletWebServerApplicationContext - - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.context.ApplicationContextException: Failed to start bean 'webServerStartStop' -2025-07-26 13:03:28 [restartedMain] INFO  o.d.c.core.config.ThreadPoolConfig - - ====关闭后台任务任务线程池==== -2025-07-26 13:03:28 [restartedMain] INFO  c.b.d.d.DynamicRoutingDataSource - - dynamic-datasource start closing .... -2025-07-26 13:03:28 [restartedMain] INFO  com.zaxxer.hikari.HikariDataSource - - master - Shutdown initiated... -2025-07-26 13:03:28 [restartedMain] INFO  com.zaxxer.hikari.HikariDataSource - - master - Shutdown completed. -2025-07-26 13:03:28 [restartedMain] INFO  c.b.d.d.d.DefaultDataSourceDestroyer - - dynamic-datasource close the datasource named [master] success, -2025-07-26 13:03:28 [restartedMain] INFO  c.b.d.d.DynamicRoutingDataSource - - dynamic-datasource all closed success,bye -2025-07-26 13:03:28 [restartedMain] ERROR o.s.b.d.LoggingFailureAnalysisReporter - - - -*************************** -APPLICATION FAILED TO START -*************************** - -Description: - -Web server failed to start. Port 8080 was already in use. - -Action: - -Identify and stop the process that's listening on port 8080 or configure this application to listen on another port. - diff --git a/backend/quota_diagnosis_results.txt b/backend/quota_diagnosis_results.txt deleted file mode 100644 index 608d9f21..00000000 --- a/backend/quota_diagnosis_results.txt +++ /dev/null @@ -1,37 +0,0 @@ -mysql: [Warning] Using a password on the command line interface can be insecure. -info -=== 1. 班主任和班级信息检查 === -info -=== 2. 奖学金批次信息检查 === -batch_id batch_name batch_status start_time end_time create_time -1952730449122004993 2025-2026??????????????? ACTIVE 2025-08-05 00:00:00 2025-08-29 00:00:00 2025-08-05 21:56:34 -1 2024-2025??????????? ACTIVE 2024-09-01 00:00:00 2024-12-31 23:59:59 2025-08-05 09:04:39 -info -=== 3. 奖学金类型信息检查 === -type_id type_name level_type type_status batch_id batch_name -1 ??????? FIRST 1 1953330495479644161 2025-2026????????????? -1 ??????? FIRST 1 1952730449122004993 2025-2026??????????????? -5 ????????? FIRST 1 1952730449122004993 2025-2026??????????????? -1953004971846852609 ??????? FIRST 1 1952730449122004993 2025-2026??????????????? -6 ????????? FIRST 1 NULL NULL -2 ??????? SECOND 1 1953330495479644161 2025-2026????????????? -2 ??????? SECOND 1 1952730449122004993 2025-2026??????????????? -1953005237862195202 ??????? SECOND 1 1952730449122004993 2025-2026??????????????? -7 ????????? SECOND 1 NULL NULL -1953004602014097409 ??????? SPECIAL 1 1952730449122004993 2025-2026??????????????? -4 ????????? SPECIAL 1 1952730449122004993 2025-2026??????????????? -8 ??????? SPECIAL 1 NULL NULL -1952634577994387457 ????? SPECIAL 1 1952730449122004993 2025-2026??????????????? -1953004324552499202 ?????? SPECIAL 1 1952730449122004993 2025-2026??????????????? -3 ??????? THIRD 1 1952730449122004993 2025-2026??????????????? -1953005415696490497 ??????? THIRD 1 1952730449122004993 2025-2026??????????????? -info -=== 4. 班级名额分配情况检查 === -quota_id batch_id batch_name scholarship_type_id type_name class_id class_name total_quota used_quota available_quota allocation_method status create_time -2001 1 2024-2025??????????? 1 ??????? 1001 NULL 2 0 2 MANUAL ACTIVE 2025-08-06 11:00:49 -2003 1 2024-2025??????????? 2 ??????? 1001 NULL 3 0 2 MANUAL ACTIVE 2025-08-06 11:00:49 -2002 1 2024-2025??????????? 1 ??????? 1002 NULL 1 0 1 MANUAL ACTIVE 2025-08-06 11:00:49 -2004 1 2024-2025??????????? 2 ??????? 1002 NULL 2 0 2 MANUAL ACTIVE 2025-08-06 11:00:49 -info -=== 5. 已使用名额情况检查 === -ERROR 1054 (42S22) at line 87: Unknown column 'sa.applicant_class_id' in 'field list' diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedMapper.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedMapper.java index 864f92d6..f26684ca 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedMapper.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgDormBedMapper.java @@ -43,13 +43,13 @@ public interface XgDormBedMapper extends BaseMapperPlus bs.status as assignment_status, bs.college_id, - c.college_name, + college_dept.dept_name as college_name, bs.major_id, - c.major_name, + major_dept.dept_name as major_name, bs.class_id, - c.class_name, + class_dept.dept_name as class_name, c.class_code, c.grade, @@ -62,6 +62,9 @@ public interface XgDormBedMapper extends BaseMapperPlus FROM xg_dorm_bed b LEFT JOIN xg_dorm_room r ON b.room_id = r.id LEFT JOIN xg_dorm_bed_student bs ON b.id = bs.bed_id AND bs.status = '0' AND bs.deleted = '0' + LEFT JOIN sys_dept college_dept ON bs.college_id = college_dept.dept_id AND college_dept.del_flag = '0' + LEFT JOIN sys_dept major_dept ON bs.major_id = major_dept.dept_id AND major_dept.del_flag = '0' + LEFT JOIN sys_dept class_dept ON bs.class_id = class_dept.dept_id AND class_dept.del_flag = '0' LEFT JOIN xg_class_msg c ON bs.class_id = c.id AND c.deleted = 0 LEFT JOIN xg_student_msg s ON bs.student_id = s.id AND s.deleted = 0 WHERE b.room_id = #{roomId} @@ -96,13 +99,13 @@ public interface XgDormBedMapper extends BaseMapperPlus bs.status as assignment_status, bs.college_id, - c.college_name, + college_dept.dept_name as college_name, bs.major_id, - c.major_name, + major_dept.dept_name as major_name, bs.class_id, - c.class_name, + class_dept.dept_name as class_name, c.class_code, c.grade, @@ -115,6 +118,9 @@ public interface XgDormBedMapper extends BaseMapperPlus FROM xg_dorm_bed b LEFT JOIN xg_dorm_room r ON b.room_id = r.id LEFT JOIN xg_dorm_bed_student bs ON b.id = bs.bed_id AND bs.status = '0' AND bs.deleted = '0' + LEFT JOIN sys_dept college_dept ON bs.college_id = college_dept.dept_id AND college_dept.del_flag = '0' + LEFT JOIN sys_dept major_dept ON bs.major_id = major_dept.dept_id AND major_dept.del_flag = '0' + LEFT JOIN sys_dept class_dept ON bs.class_id = class_dept.dept_id AND class_dept.del_flag = '0' LEFT JOIN xg_class_msg c ON bs.class_id = c.id AND c.deleted = 0 LEFT JOIN xg_student_msg s ON bs.student_id = s.id AND s.deleted = 0 WHERE r.building_id = #{buildingId} diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedStudentServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedStudentServiceImpl.java index 3363dc82..c3b59f55 100644 --- a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedStudentServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormBedStudentServiceImpl.java @@ -234,12 +234,15 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { } } - // 验证学院人数总和是否等于床位总数 + // 验证学院人数总和不能超过床位总数(允许少于床位总数) int totalStudents = collegeCounts.stream().mapToInt(Integer::intValue).sum(); - if (totalStudents != totalBeds) { - throw new RuntimeException("学院人数总和(" + totalStudents + ")与床位总数(" + totalBeds + ")不匹配"); + if (totalStudents > totalBeds) { + throw new RuntimeException("学院人数总和(" + totalStudents + ")超过了床位总数(" + totalBeds + "),请调整分配数量"); } + log.info("床位分配验证通过:总床位数{},分配学生数{},剩余床位数{}", + totalBeds, totalStudents, totalBeds - totalStudents); + // 房间使用状态跟踪 int[] roomUsage = new int[sortedRoomIds.size()]; // 记录每个房间已使用床位数 Map roomCollegeMap = new java.util.HashMap<>(); // 记录房间主要分配给哪个学院 @@ -352,8 +355,12 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { log.info("学院{}床位分配完成", collegeId); } - // 6. 更新床位状态为已分配 - for (Long bedId : bo.getBedIds()) { + // 6. 更新床位状态为已分配(只更新实际被分配的床位) + Set allocatedBedIds = allocationRecords.stream() + .map(XgDormBedStudent::getBedId) + .collect(java.util.stream.Collectors.toSet()); + + for (Long bedId : allocatedBedIds) { XgDormBed updateBed = new XgDormBed(); updateBed.setId(bedId); updateBed.setBedStatus("1"); // 1-已分配 @@ -361,6 +368,8 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { xgDormBedMapper.updateById(updateBed); } + log.info("更新床位状态:共{}个床位被标记为已分配", allocatedBedIds.size()); + // 7. 批量插入分配记录 if (!allocationRecords.isEmpty()) { boolean insertResult = baseMapper.insertBatch(allocationRecords); @@ -369,8 +378,8 @@ public class XgDormBedStudentServiceImpl implements IXgDormBedStudentService { } } - log.info("成功分配{}个床位给{}个学院,分配记录:{}", - totalBeds, bo.getCollegeIds().size(), allocationRecords.size()); + log.info("成功分配{}个床位给{}个学院,总可用床位:{},分配记录:{}", + allocatedBedIds.size(), bo.getCollegeIds().size(), totalBeds, allocationRecords.size()); return true; } catch (Exception e) { diff --git a/web/src/api/xg/xgss/dormBed/index.ts b/web/src/api/xg/xgss/dormBed/index.ts index e8fcc569..f2a9b801 100644 --- a/web/src/api/xg/xgss/dormBed/index.ts +++ b/web/src/api/xg/xgss/dormBed/index.ts @@ -91,6 +91,7 @@ export const getBedDetailsByBuildingId = (buildingId: string | number): AxiosPro export const batchAllocateBedsToCollege = (data: { bedIds: (string | number)[]; collegeIds: (string | number)[]; + collegeStudentCounts?: number[]; buildingId?: string | number; buildingType?: string; }) => { diff --git a/web/src/views/xg/xgss/xsssfpejxy/components/CollegeAllocationDialog.vue b/web/src/views/xg/xgss/xsssfpejxy/components/CollegeAllocationDialog.vue index 3e185ad7..78570767 100644 --- a/web/src/views/xg/xgss/xsssfpejxy/components/CollegeAllocationDialog.vue +++ b/web/src/views/xg/xgss/xsssfpejxy/components/CollegeAllocationDialog.vue @@ -14,6 +14,10 @@ {{ buildingInfo.buildingType === '1' ? '男生楼' : '女生楼' }}:{{ buildingInfo.label }} +
+ 可分配:{{ availableBedsCount }} 个 + 已分配:{{ allocatedBedsCount }} 个 + 维修中:{{ maintenanceBedsCount }} 个 @@ -29,12 +33,26 @@ @@ -137,8 +155,8 @@ { type: 'number', min: 0, - max: props.selectedBeds.length, - message: `床位数必须在1-${props.selectedBeds.length}之间`, + max: availableBedsCount, + message: `床位数必须在0-${availableBedsCount}之间`, trigger: 'blur' } ]" @@ -146,7 +164,7 @@ { return roomNames.size; }); -// 计算已分配的床位数 +// 计算各状态床位数量 +const availableBedsCount = computed(() => { + return props.selectedBeds.filter((bed) => bed.bedStatus === '0').length; +}); + const allocatedBedsCount = computed(() => { + return props.selectedBeds.filter((bed) => bed.bedStatus === '1').length; +}); + +const maintenanceBedsCount = computed(() => { + return props.selectedBeds.filter((bed) => bed.bedStatus === '2').length; +}); + +// 获取可分配的床位(只有空闲状态的床位) +const availableBeds = computed(() => { + return props.selectedBeds.filter((bed) => bed.bedStatus === '0'); +}); + +// 计算已分配的床位数(学院分配表单中的) +const formAllocatedBedsCount = computed(() => { return Object.values(allocationForm.value).reduce((sum, count) => sum + (count || 0), 0); }); -// 计算剩余床位数 +// 计算剩余可分配床位数(基于空闲床位) const remainingBedsCount = computed(() => { - return props.selectedBeds.length - allocatedBedsCount.value; + return availableBedsCount.value - formAllocatedBedsCount.value; }); // 构建床位树形数据 @@ -263,11 +299,19 @@ const buildBedTreeData = () => { // 构建树形结构 const treeData = []; roomGroups.forEach((beds, roomName) => { + // 统计房间内各状态床位数量 + const availableCount = beds.filter((bed) => bed.bedStatus === '0').length; + const allocatedCount = beds.filter((bed) => bed.bedStatus === '1').length; + const maintenanceCount = beds.filter((bed) => bed.bedStatus === '2').length; + const roomNode = { id: `room-${roomName}`, label: roomName, type: 'room', bedCount: beds.length, + availableCount, + allocatedCount, + maintenanceCount, children: beds.map((bed) => ({ id: bed.id, label: `${bed.bedName}`, @@ -321,6 +365,17 @@ const getBedNodeClass = (data: any) => { } } else if (data.type === 'bed') { classes.push('bed-node'); + + // 根据床位状态添加样式 + if (data.bedData?.bedStatus === '0') { + classes.push('available-bed-node'); // 可分配床位 + } else if (data.bedData?.bedStatus === '1') { + classes.push('allocated-bed-node'); // 已分配床位 + } else if (data.bedData?.bedStatus === '2') { + classes.push('maintenance-bed-node'); // 维修中床位 + } + + // 根据宿舍楼类型添加颜色主题 if (props.buildingInfo?.buildingType === '1') { classes.push('male-bed-node'); } else if (props.buildingInfo?.buildingType === '2') { @@ -381,16 +436,19 @@ const handleCheckChange = (data: XgStudentTreeVO, checked: boolean) => { return; } - // 检查是否还有剩余床位可分配 - if (checked && props.selectedBeds.length === 0) { - // 如果没有床位可分配,阻止勾选并给出提示 + // 检查是否还有可分配的床位 + if (checked && availableBedsCount.value === 0) { + // 如果没有可分配床位,阻止勾选并给出提示 nextTick(() => { collegeTreeRef.value?.setChecked(data.id, false, false); }); - ElMessage.warning('当前没有床位可分配,请先选择床位'); + ElMessage.warning('当前没有可分配的床位,只有空闲状态的床位才能分配'); return; } + // 获取之前的选中学院列表 + const previousCollegeIds = [...selectedCollegeIds.value]; + // 更新选中的学院ID列表 const checkedKeys = collegeTreeRef.value?.getCheckedKeys() || []; selectedCollegeIds.value = checkedKeys.filter((key: any) => { @@ -398,11 +456,11 @@ const handleCheckChange = (data: XgStudentTreeVO, checked: boolean) => { return node?.data?.type === 'college'; }); - // 初始化人数分配表单 - initAllocationForm(); + // 只为新增的学院分配床位,保留已有学院的用户修改 + updateAllocationFormForNewColleges(previousCollegeIds, selectedCollegeIds.value); }; -// 初始化人数分配表单 +// 初始化人数分配表单(首次打开对话框时使用) const initAllocationForm = () => { allocationForm.value = {}; @@ -410,25 +468,25 @@ const initAllocationForm = () => { return; } - // 检查是否还有床位可分配 - if (props.selectedBeds.length === 0) { - // 如果没有床位可分配,清空选中的学院并给出提示 + // 检查是否还有可分配的床位 + if (availableBedsCount.value === 0) { + // 如果没有可分配床位,清空选中的学院并给出提示 selectedCollegeIds.value = []; nextTick(() => { // 取消所有勾选状态 collegeTreeRef.value?.setCheckedKeys([]); }); - ElMessage.warning('当前没有床位可分配,请先选择床位'); + ElMessage.warning('当前没有可分配的床位,只有空闲状态的床位才能分配'); return; } - // 新的床位分配规则:尽量满足学院需求 + // 新的床位分配规则:基于可分配床位(空闲状态)尽量满足学院需求 // 1. 按学院顺序逐个分配 - // 2. 如果学院需求床位数 <= 剩余床位数,则满足学院需求 - // 3. 如果学院需求床位数 > 剩余床位数,则分配所有剩余床位 + // 2. 如果学院需求床位数 <= 剩余可分配床位数,则满足学院需求 + // 3. 如果学院需求床位数 > 剩余可分配床位数,则分配所有剩余可分配床位 - const totalBeds = props.selectedBeds.length; - let remainingBeds = totalBeds; + const totalAvailableBeds = availableBedsCount.value; + let remainingBeds = totalAvailableBeds; // 获取学院的真实床位需求数(总需求数 - 已分配数) const getCollegeDemand = (collegeId: string | number) => { @@ -475,6 +533,85 @@ const initAllocationForm = () => { }); }; +// 更新学院分配表单(只为新增学院分配床位,保留已有学院的用户修改) +const updateAllocationFormForNewColleges = (previousCollegeIds: (string | number)[], currentCollegeIds: (string | number)[]) => { + // 检查是否还有可分配的床位 + if (availableBedsCount.value === 0) { + // 如果没有可分配床位,清空选中的学院并给出提示 + selectedCollegeIds.value = []; + allocationForm.value = {}; + nextTick(() => { + // 取消所有勾选状态 + collegeTreeRef.value?.setCheckedKeys([]); + }); + ElMessage.warning('当前没有可分配的床位,只有空闲状态的床位才能分配'); + return; + } + + // 获取学院的真实床位需求数(总需求数 - 已分配数) + const getCollegeDemand = (collegeId: string | number) => { + // 从学院树数据中查找学院节点 + const college = findCollegeById(collegeTreeData.value, collegeId); + if (!college) return 0; + + // 根据宿舍楼类型确定性别 + if (props.buildingInfo?.buildingType === '1') { + // 男生楼,计算男生床位需求 + const maleDemand = (college.maleTotal || 0) - (college.maleAssigned || 0); + return Math.max(0, maleDemand); // 确保不为负数 + } else if (props.buildingInfo?.buildingType === '2') { + // 女生楼,计算女生床位需求 + const femaleDemand = (college.femaleTotal || 0) - (college.femaleAssigned || 0); + return Math.max(0, femaleDemand); // 确保不为负数 + } else { + // 混合楼,计算总床位需求 + const totalDemand = (college.maleTotal || 0) + (college.femaleTotal || 0) - (college.maleAssigned || 0) - (college.femaleAssigned || 0); + return Math.max(0, totalDemand); // 确保不为负数 + } + }; + + // 找出新增的学院和取消勾选的学院 + const addedColleges = currentCollegeIds.filter((id) => !previousCollegeIds.includes(id)); + const removedColleges = previousCollegeIds.filter((id) => !currentCollegeIds.includes(id)); + + // 移除取消勾选的学院 + removedColleges.forEach((collegeId) => { + delete allocationForm.value[collegeId]; + }); + + // 如果有新增的学院,为其分配床位 + if (addedColleges.length > 0) { + // 计算已分配的床位数(保留用户手动修改的数据) + const allocatedBeds = Object.keys(allocationForm.value) + .filter((collegeId) => currentCollegeIds.includes(collegeId)) + .reduce((sum, collegeId) => sum + (allocationForm.value[collegeId] || 0), 0); + + let remainingBeds = availableBedsCount.value - allocatedBeds; + + // 为新增的学院分配床位 + addedColleges.forEach((collegeId) => { + let bedCount = 0; + + if (remainingBeds > 0) { + // 获取学院的真实床位需求数 + const collegeDemand = getCollegeDemand(collegeId); + + if (remainingBeds >= collegeDemand) { + // 如果剩余床位数 >= 学院需求,则满足学院需求 + bedCount = collegeDemand; + remainingBeds -= collegeDemand; + } else { + // 如果剩余床位数 < 学院需求,则分配所有剩余床位 + bedCount = remainingBeds; + remainingBeds = 0; + } + } + + allocationForm.value[collegeId] = bedCount; + }); + } +}; + // 获取学院树数据 const getCollegeTreeData = async () => { try { @@ -554,12 +691,12 @@ const handleAllocationChange = () => { // 验证床位分配是否超出总床位数 const totalAllocated = Object.values(allocationForm.value).reduce((sum, count) => sum + (count || 0), 0); - if (totalAllocated > props.selectedBeds.length) { - ElMessage.warning(`床位分配超出:已分配 ${totalAllocated} 个,但只有 ${props.selectedBeds.length} 个床位可用`); + if (totalAllocated > availableBedsCount.value) { + ElMessage.warning(`床位分配超出:已分配 ${totalAllocated} 个,但只有 ${availableBedsCount.value} 个可分配床位`); } console.log('学院人数分配变化:', allocationForm.value); - console.log('总分配床位数:', totalAllocated, '总可用床位数:', props.selectedBeds.length); + console.log('总分配床位数:', totalAllocated, '总可分配床位数:', availableBedsCount.value); }; // 获取分配统计信息标题 @@ -576,11 +713,11 @@ const getAllocationStatsTitle = () => { // 获取分配统计信息描述 const getAllocationStatsDescription = () => { if (remainingBedsCount.value === 0) { - return `已分配 ${allocatedBedsCount.value} 个床位,分配完成!`; + return `已分配 ${formAllocatedBedsCount.value} 个床位,分配完成!`; } else if (remainingBedsCount.value > 0) { - return `已分配 ${allocatedBedsCount.value} 个床位,还需分配 ${remainingBedsCount.value} 个床位`; + return `已分配 ${formAllocatedBedsCount.value} 个床位,还可以分配 ${remainingBedsCount.value} 个床位`; } else { - return `已分配 ${allocatedBedsCount.value} 个床位,超出 ${Math.abs(remainingBedsCount.value)} 个床位`; + return `已分配 ${formAllocatedBedsCount.value} 个床位,超出 ${Math.abs(remainingBedsCount.value)} 个床位`; } }; @@ -618,15 +755,15 @@ const handleConfirm = async () => { return; } - if (props.selectedBeds.length === 0) { - ElMessage.warning('请选择要分配的床位'); + if (availableBedsCount.value === 0) { + ElMessage.warning('请选择可分配的床位(只有空闲状态的床位才能分配)'); return; } // 验证学院人数分配 const totalAllocated = Object.values(allocationForm.value).reduce((sum, count) => sum + (count || 0), 0); - if (totalAllocated !== props.selectedBeds.length) { - ElMessage.warning(`床位分配不匹配:已分配 ${totalAllocated} 个,但选择了 ${props.selectedBeds.length} 个床位`); + if (totalAllocated > availableBedsCount.value) { + ElMessage.warning(`床位分配超出:已分配 ${totalAllocated} 个,但只有 ${availableBedsCount.value} 个可分配床位`); return; } @@ -639,23 +776,50 @@ const handleConfirm = async () => { // } try { - await ElMessageBox.confirm(`确定要将 ${props.selectedBeds.length} 个床位分配给 ${selectedCollegeIds.value.length} 个学院吗?`, '确认分配', { + // 构建详细的分配信息提示 + const allocationDetails = selectedCollegeIds.value + .map((collegeId) => { + const collegeName = getCollegeName(collegeId); + const bedCount = allocationForm.value[collegeId] || 0; + return ` • ${collegeName}:${bedCount} 个床位`; + }) + .join('\n'); + + const confirmMessage = `确定要分配以下床位吗?\n\n总选择床位:${props.selectedBeds.length} 个\n可分配床位:${availableBedsCount.value} 个\n总分配数:${totalAllocated} 个\n\n分配详情:\n${allocationDetails}`; + + await ElMessageBox.confirm(confirmMessage, '确认分配', { confirmButtonText: '确定分配', cancelButtonText: '取消', - type: 'warning' + type: 'warning', + dangerouslyUseHTMLString: false }); confirmLoading.value = true; - // 构建分配参数,包含学院人数信息 + // 获取真正的学院ID列表和对应的学生人数(从树节点数据中提取collegeId字段) + const realCollegeIds: number[] = []; + const collegeStudentCounts: number[] = []; + + selectedCollegeIds.value.forEach((treeNodeId) => { + const college = findCollegeById(collegeTreeData.value, treeNodeId); + if (!college?.collegeId) { + throw new Error(`学院"${getCollegeName(treeNodeId)}"的ID信息不完整`); + } + realCollegeIds.push(college.collegeId); + collegeStudentCounts.push(allocationForm.value[treeNodeId] || 0); + }); + + // 构建分配参数,只使用可分配的床位(空闲状态) const allocationData = { - bedIds: props.selectedBeds.map((bed) => bed.id), - collegeIds: selectedCollegeIds.value, - collegeStudentCounts: selectedCollegeIds.value.map((collegeId) => allocationForm.value[collegeId]), + bedIds: availableBeds.value.map((bed) => bed.id), // 只传递可分配的床位ID + collegeIds: realCollegeIds, // 使用真正的学院ID + collegeStudentCounts: collegeStudentCounts, // 与学院ID一一对应的学生人数 buildingId: props.buildingInfo?.id, buildingType: props.buildingInfo?.buildingType }; + console.log('床位分配参数:', allocationData); + // 调用批量分配API await batchAllocateBedsToCollege(allocationData); @@ -711,6 +875,20 @@ watch( }); ElMessage.info('床位已清空,已自动清空学院选择'); } + + // 如果没有可分配床位,自动清空已选择的学院 + const newAvailableCount = newBeds.filter((bed) => bed.bedStatus === '0').length; + const oldAvailableCount = oldBeds ? oldBeds.filter((bed) => bed.bedStatus === '0').length : 0; + + if (newAvailableCount === 0 && oldAvailableCount > 0) { + selectedCollegeIds.value = []; + allocationForm.value = {}; + nextTick(() => { + // 取消所有勾选状态 + collegeTreeRef.value?.setCheckedKeys([]); + }); + ElMessage.warning('没有可分配的床位,已自动清空学院选择'); + } }, { deep: true } ); @@ -788,12 +966,10 @@ watch( background-color: rgba(64, 158, 255, 0.1); padding: 2px 6px; border-radius: 4px; - /* border-left: 3px solid var(--el-color-primary); */ } .male-bed-node { color: var(--el-color-primary); - background-color: rgba(64, 158, 255, 0.05); padding: 1px 4px; border-radius: 3px; } @@ -804,18 +980,66 @@ watch( background-color: rgba(255, 105, 180, 0.1); padding: 2px 6px; border-radius: 4px; - /* border-left: 3px solid #ff69b4; */ } .female-bed-node { color: #ff69b4; - background-color: rgba(255, 105, 180, 0.05); padding: 1px 4px; border-radius: 3px; } +/* 床位状态样式 */ +.available-bed-node { + background-color: rgba(103, 194, 58, 0.1) !important; + color: var(--el-color-success) !important; + font-weight: 500; + /* border-left: 3px solid var(--el-color-success); */ + padding-left: 8px !important; +} + +.allocated-bed-node { + background-color: rgba(230, 162, 60, 0.1) !important; + color: var(--el-color-warning) !important; + opacity: 0.7; +} + +.maintenance-bed-node { + background-color: rgba(245, 108, 108, 0.1) !important; + color: var(--el-color-danger) !important; + opacity: 0.7; + text-decoration: line-through; +} + .bed-count { font-size: 12px; + display: flex; + gap: 4px; + align-items: center; +} + +.bed-status-indicator { + display: inline-flex; + align-items: center; +} + +/* 学院名称标签样式 */ +.college-name { + display: inline-flex; + align-items: center; +} + +.college-tag { + background-color: rgba(144, 147, 153, 0.1) !important; + border-color: rgba(144, 147, 153, 0.3) !important; + color: var(--el-text-color-regular) !important; + font-size: 11px; + padding: 2px 6px; + border-radius: 3px; +} + +.college-tag .el-icon-school { + font-size: 10px; + margin-right: 2px; } /* 床位数量标签样式 */ diff --git a/web/src/views/xg/xgss/xsssfpejxy/index.vue b/web/src/views/xg/xgss/xsssfpejxy/index.vue index cb4c9146..dae7d637 100644 --- a/web/src/views/xg/xgss/xsssfpejxy/index.vue +++ b/web/src/views/xg/xgss/xsssfpejxy/index.vue @@ -149,12 +149,18 @@ - 房间管理 - - 已选择: {{ selectedRoomCount }}个房间 / {{ selectedBedCount }}个床位 - - (房间选择时会自动包含其下所有空闲床位) +
+ 已选择: {{ selectedRoomCount }}个房间 / {{ selectedBedCount }}个床位 + + [可分配: {{ selectedBedStats.available }}个, 已分配: {{ selectedBedStats.allocated }}个, 维修中: {{ selectedBedStats.maintenance }}个] - + + (房间选择时会自动包含其下所有床位) + +
@@ -174,9 +180,10 @@ table-layout="auto" @selection-change="handleSelectionChange" @select-all="handleSelectAll" + :row-class-name="getRowClassName" > - + - +