diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/IATDManageAppService.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/IATDManageAppService.java index f8853a27d..93a479741 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/IATDManageAppService.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/IATDManageAppService.java @@ -130,5 +130,14 @@ public interface IATDManageAppService { * @return 转科筛选选项 */ R getTransferOptions(); + + /** + * 换床 (指定目标床位) + * + * @param encounterId 住院患者id + * @param targetBedId 目标床位id + * @return 结果 + */ + R changeBedAssginment(Long encounterId, Long targetBedId); } diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/impl/ATDManageAppServiceImpl.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/impl/ATDManageAppServiceImpl.java index 8a72a39a2..16301cbf8 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/impl/ATDManageAppServiceImpl.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/appservice/impl/ATDManageAppServiceImpl.java @@ -16,6 +16,7 @@ import com.healthlink.his.administration.domain.Encounter; import com.healthlink.his.administration.domain.ChargeItem; import com.healthlink.his.administration.service.IChargeItemService; import com.healthlink.his.administration.domain.EncounterLocation; +import com.healthlink.his.administration.domain.Location; import com.healthlink.his.administration.domain.EncounterParticipant; import com.healthlink.his.administration.domain.Location; import com.healthlink.his.administration.domain.Organization; @@ -224,7 +225,7 @@ public class ATDManageAppServiceImpl implements IATDManageAppService { public R getAdmissionBedPage(AdmissionPageParam admissionPageParam, Integer pageNo, Integer pageSize) { // 获取当前登录用户的科室 ID Long currentUserOrgId = SecurityUtils.getLoginUser().getOrgId(); - + // 构建查询条件 QueryWrapper queryWrapper = HisQueryUtils.buildQueryWrapper(admissionPageParam, null, null, null); @@ -528,7 +529,7 @@ public class ATDManageAppServiceImpl implements IATDManageAppService { List savedParticipants = encounterParticipantService.getEncounterParticipantList(encounterId); log.info("保存后查询参与者 - encounterId: {}, 数量: {}", encounterId, savedParticipants.size()); for (EncounterParticipant ep : savedParticipants) { - log.info("参与者详情 - typeCode: {}, practitionerId: {}, statusEnum: {}", + log.info("参与者详情 - typeCode: {}, practitionerId: {}, statusEnum: {}", ep.getTypeCode(), ep.getPractitionerId(), ep.getStatusEnum()); } // 更新入院体征(在事务外执行,避免影响参与者数据保存) @@ -983,7 +984,7 @@ public class ATDManageAppServiceImpl implements IATDManageAppService { = ((List) docStatisticsAppService.queryByEncounterId(encounterId).getData()).stream() .filter(item -> DocDefinitionEnum.ADMISSION_VITAL_SIGNS.getValue().equals(item.getSource())).toList(); List list = new ArrayList<>(data); - + // 先删除所有已有的入院体征记录(重新保存最新数据) for (DocStatisticsDto existingItem : data) { if (existingItem.getId() != null) { @@ -991,7 +992,7 @@ public class ATDManageAppServiceImpl implements IATDManageAppService { } } list.clear(); - + map.keySet().forEach(key -> { String value = map.get(key); // 只保存非空值 @@ -1188,5 +1189,143 @@ public class ATDManageAppServiceImpl implements IATDManageAppService { return R.ok("退床成功"); } + + /** + * 换床 + * + * @param encounterId 住院患者id + * @return 结果 + */ + + @Override + @Transactional(rollbackFor = Exception.class) + public R changeBedAssginment(Long encounterId, Long targetBedId) { + if (encounterId == null) { + return R.fail("换床失败,请选择有效的就诊记录"); + } + if (targetBedId == null) { + return R.fail("换床失败,请选择目标床位"); + } + Encounter encounter = encounterService.getById(encounterId); + if (encounter == null) { + return R.fail("未找到就诊记录"); + } + // 仅已入院状态允许换床 + if (!EncounterZyStatus.ADMITTED_TO_THE_HOSPITAL.getValue().equals(encounter.getStatusEnum())) { + return R.fail("该患者未在科,无法办理换床"); + } + + // 查询目标床位 + Location targetBed = locationService.getById(targetBedId); + if (targetBed == null) { + return R.fail("目标床位不存在"); + } + if (!LocationForm.BED.getValue().equals(targetBed.getFormEnum())) { + return R.fail("所选位置不是床位"); + } + + // 根据目标床位的 busNo 获取其父级房间 (house) + String bedBusNo = targetBed.getBusNo(); + if (bedBusNo == null || !bedBusNo.contains(".")) { + return R.fail("目标床位编码异常"); + } + String[] parts = bedBusNo.split("\\."); + if (parts.length < 2) { + return R.fail("目标床位编码层级异常"); + } + String houseBusNo = parts[0] + "." + parts[1]; + Location targetHouse = locationService.lambdaQuery() + .eq(Location::getBusNo, houseBusNo) + .eq(Location::getFormEnum, LocationForm.HOUSE.getValue()) + .eq(Location::getDeleteFlag, "0") + .one(); + if (targetHouse == null) { + return R.fail("未找到目标床位所属的病房"); + } + + Date now = new Date(); + + // 检查目标床位是否已经被占用 + List occupiedBedLocs = encounterLocationService.lambdaQuery() + .eq(EncounterLocation::getLocationId, targetBedId) + .eq(EncounterLocation::getFormEnum, LocationForm.BED.getValue()) + .eq(EncounterLocation::getStatusEnum, EncounterActivityStatus.ACTIVE.getValue()) + .eq(EncounterLocation::getDeleteFlag, "0") + .list(); + + if (occupiedBedLocs != null && !occupiedBedLocs.isEmpty()) { + // Target bed is occupied! This is a bed swap (床位互换) + Long targetEncounterId = occupiedBedLocs.get(0).getEncounterId(); + Encounter targetEncounter = encounterService.getById(targetEncounterId); + if (targetEncounter == null) { + return R.fail("目标床位占用患者就诊记录异常"); + } + if (!EncounterZyStatus.ADMITTED_TO_THE_HOSPITAL.getValue().equals(targetEncounter.getStatusEnum())) { + return R.fail("目标床位占用患者已不在科,无法办理换床"); + } + + // 获取当前患者的原床位和原病房 + List currentBedLocs = encounterLocationService.getEncounterLocationList(encounterId, + LocationForm.BED, EncounterActivityStatus.ACTIVE); + if (currentBedLocs == null || currentBedLocs.isEmpty()) { + return R.fail("当前患者未分配床位,无法进行换床互换"); + } + Long currentBedId = currentBedLocs.get(0).getLocationId(); + + List currentHouseLocs = encounterLocationService.getEncounterLocationList(encounterId, + LocationForm.HOUSE, EncounterActivityStatus.ACTIVE); + if (currentHouseLocs == null || currentHouseLocs.isEmpty()) { + return R.fail("当前患者原病房记录不存在"); + } + Long currentHouseId = currentHouseLocs.get(0).getLocationId(); + + // 获取被交换患者的原开始时间,保证其床位历史记录连贯性 + Date targetStartTime = occupiedBedLocs.get(0).getStartTime(); + if (targetStartTime == null) { + targetStartTime = now; + } + + // 1. 将两位患者现有的 BED 和 HOUSE 位置状态设为 COMPLETED (false) + Integer res1 = encounterLocationService.updateEncounterLocationStatus(encounterId, false); + Integer res2 = encounterLocationService.updateEncounterLocationStatus(targetEncounterId, false); + if (res1 == 0 || res2 == 0) { + throw new RuntimeException("更新原就诊位置状态失败"); + } + + // 2. 为当前患者创建新位置 (目标病房和目标床位) + encounterLocationService.creatEncounterLocation(encounterId, now, targetHouse.getId(), LocationForm.HOUSE.getValue()); + encounterLocationService.creatEncounterLocation(encounterId, now, targetBedId, LocationForm.BED.getValue()); + + // 3. 为被交换患者创建新位置 (当前患者的原病房和原床位) + encounterLocationService.creatEncounterLocation(targetEncounterId, targetStartTime, currentHouseId, LocationForm.HOUSE.getValue()); + encounterLocationService.creatEncounterLocation(targetEncounterId, targetStartTime, currentBedId, LocationForm.BED.getValue()); + + return R.ok("床位互换成功"); + } else { + // Target bed is vacant! Normal bed change + // 获取当前患者原床位 + List currentBedLocs = encounterLocationService.getEncounterLocationList(encounterId, + LocationForm.BED, EncounterActivityStatus.ACTIVE); + + // 1. 将当前患者现有的 BED 和 HOUSE 位置状态设为 COMPLETED (false) + encounterLocationService.updateEncounterLocationStatus(encounterId, false); + + // 2. 将原床位状态更新为空闲 (LocationStatus.IDLE) + if (currentBedLocs != null && !currentBedLocs.isEmpty()) { + for (EncounterLocation bedLoc : currentBedLocs) { + locationService.updateStatusById(bedLoc.getLocationId(), LocationStatus.IDLE.getValue()); + } + } + + // 3. 为当前患者创建新位置 (目标病房和目标床位) + encounterLocationService.creatEncounterLocation(encounterId, now, targetHouse.getId(), LocationForm.HOUSE.getValue()); + encounterLocationService.creatEncounterLocation(encounterId, now, targetBedId, LocationForm.BED.getValue()); + + // 4. 将目标床位状态更新为占用 (LocationStatus.OCCUPY) + locationService.updateStatusById(targetBedId, LocationStatus.OCCUPY.getValue()); + + return R.ok("换床成功"); + } + } } diff --git a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/controller/ATDManageController.java b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/controller/ATDManageController.java index 60b8ebf62..66a6c26bb 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/controller/ATDManageController.java +++ b/healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/inhospitalnursestation/controller/ATDManageController.java @@ -14,6 +14,8 @@ import org.springframework.web.bind.annotation.*; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; /** * 入出转管理 controller @@ -187,5 +189,19 @@ public class ATDManageController { public R getTransferOptions() { return atdManageAppService.getTransferOptions(); } + + /** + * 换床 + * + * @param encounterId 住院患者id + * @return 结果 + */ + @PutMapping(value = "/change-bed-assignment") + public R changeBedAssignment(Long encounterId){ + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); + String targetBedIdStr = request.getParameter("targetBedId"); + Long targetBedId = (targetBedIdStr == null || targetBedIdStr.trim().isEmpty()) ? null : Long.valueOf(targetBedIdStr); + return atdManageAppService.changeBedAssginment(encounterId, targetBedId); + } } diff --git a/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/inhospitalnursestation/ATDManageAppMapper.xml b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/inhospitalnursestation/ATDManageAppMapper.xml index 8e1174ffa..154b9b904 100755 --- a/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/inhospitalnursestation/ATDManageAppMapper.xml +++ b/healthlink-his-server/healthlink-his-application/src/main/resources/mapper/inhospitalnursestation/ATDManageAppMapper.xml @@ -113,7 +113,7 @@ AND ao_target.delete_flag = '0' WHERE ae.delete_flag = '0' AND ae.class_enum = #{imp} - AND ae.status_enum != #{toBeRegistered} + AND ae.status_enum IN (2, 3, 5, 6) AND ae.organization_id = #{currentUserOrgId} GROUP BY ae.tenant_id, ae.id, diff --git a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/administration/service/impl/EncounterLocationServiceImpl.java b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/administration/service/impl/EncounterLocationServiceImpl.java index 8e88c2afa..ec64ab186 100755 --- a/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/administration/service/impl/EncounterLocationServiceImpl.java +++ b/healthlink-his-server/healthlink-his-domain/src/main/java/com/healthlink/his/administration/service/impl/EncounterLocationServiceImpl.java @@ -124,11 +124,14 @@ public class EncounterLocationServiceImpl extends ServiceImpl() .set(EncounterLocation::getStatusEnum, EncounterActivityStatus.COMPLETED.getValue()) - .eq(EncounterLocation::getEncounterId, encounterId).in(EncounterLocation::getFormEnum, locationForms)); + .eq(EncounterLocation::getEncounterId, encounterId) + .in(EncounterLocation::getFormEnum, locationForms) + .eq(EncounterLocation::getStatusEnum, EncounterActivityStatus.ACTIVE.getValue()) + .eq(EncounterLocation::getDeleteFlag, DelFlag.NO.getCode())); } /* diff --git a/healthlink-his-ui/package.json b/healthlink-his-ui/package.json index 04072c176..6f0997f3f 100755 --- a/healthlink-his-ui/package.json +++ b/healthlink-his-ui/package.json @@ -65,7 +65,7 @@ "vue": "^3.5.25", "vue-area-linkage": "^5.1.0", "vue-cropper": "^1.1.1", - "vue-i18n": "^11.4.6", + "vue-i18n": "^9.14.5", "vue-plugin-hiprint": "^0.0.60", "vue-router": "^4.6.4", "vxe-pc-ui": "^4.14.26", diff --git a/healthlink-his-ui/src/views/doctorstation/components/examination/examinationApplication.vue b/healthlink-his-ui/src/views/doctorstation/components/examination/examinationApplication.vue index 1eb377740..3399566c9 100755 --- a/healthlink-his-ui/src/views/doctorstation/components/examination/examinationApplication.vue +++ b/healthlink-his-ui/src/views/doctorstation/components/examination/examinationApplication.vue @@ -271,7 +271,8 @@ > @@ -1759,12 +1760,14 @@ watch(selectedMethods, () => { }, { deep: true }); // 监听患者变化 -watch(() => props.patientInfo, (newVal) => { - if (newVal?.encounterId) { - initPatientForm(newVal); +// 🔧 Bug #767 同类修复:移除 deep: true,避免深层属性变化时重置用户输入 +// 父组件通过整体替换 patientInfo 对象来切换患者,watch 引用变化即可,无需 deep +watch(() => props.patientInfo?.encounterId, (newEncounterId) => { + if (newEncounterId && props.patientInfo?.encounterId) { + initPatientForm(props.patientInfo); getList(); } -}, { immediate: true, deep: true }); +}, { immediate: true }); watch(() => props.activeTab, async (val) => { if (val === 'examination') { diff --git a/healthlink-his-ui/src/views/doctorstation/components/inspection/inspectionApplication.vue b/healthlink-his-ui/src/views/doctorstation/components/inspection/inspectionApplication.vue index a52538b5f..96a7e016d 100755 --- a/healthlink-his-ui/src/views/doctorstation/components/inspection/inspectionApplication.vue +++ b/healthlink-his-ui/src/views/doctorstation/components/inspection/inspectionApplication.vue @@ -2395,9 +2395,9 @@ const handleCurrentChange = (page) => { getInspectionList() } -// 选择框变化 -const handleSelectionChange = (selection) => { - selectedRows.value = selection +// 选择框变化(vxe-table v4 事件参数为 { records, reserves, row, rowIndex, checked, $event }) +const handleSelectionChange = ({ records }) => { + selectedRows.value = records || [] } const handlePrint = (row) => { @@ -2456,11 +2456,11 @@ const handleDelete = (row) => { }) } -// 单元格点击 - 点击表格行时加载申请单详情 -const handleCellClick = (row, column) => { - // 如果点击的是操作列或展开列,不触发数据填充 +// 单元格点击 - 点击表格行时加载申请单详情(vxe-table v4 事件参数为 { row, column, ... }) +const handleCellClick = ({ row, column }) => { + // 如果点击的是操作列、展开列或选择列,不触发数据填充 if (column.property === '操作' || column.label === '操作' || - column.type === 'expand' || column.type === 'selection') { + column.type === 'expand' || column.type === 'selection' || column.type === 'checkbox') { return; } // 点击表格行时,将该申请单的数据加载到表单中 @@ -2470,8 +2470,8 @@ const handleCellClick = (row, column) => { } } -// 行点击事件处理 -const handleRowClick = (currentRow, oldRow) => { +// 行点击事件处理(vxe-table v4 事件参数为 { row, rowIndex, $event }) +const handleRowClick = ({ row: currentRow }) => { // 点击表格行时,将该申请单的数据加载到表单中 // 使用 applyNo 判断是否有效,同时检查是否处于删除状态 if (currentRow && currentRow.applyNo && !isDeleting.value) { @@ -2618,24 +2618,21 @@ watch(() => props.activeTab, async (newVal) => { }) // 监听patientInfo变化,确保encounterId及时更新并重新加载数据 -watch(() => props.patientInfo, async (newVal) => { - if (newVal && newVal.encounterId) { - const oldEncounterId = queryParams.encounterId - queryParams.encounterId = newVal.encounterId +// 🔧 Bug #767 修复:移除 deep: true,避免深层属性变化时重置就诊卡号等用户输入 +// 父组件通过整体替换 patientInfo 对象来切换患者,watch 引用变化即可,无需 deep +watch(() => props.patientInfo?.encounterId, async (newEncounterId, oldEncounterId) => { + if (newEncounterId) { + queryParams.encounterId = newEncounterId // 初始化数据 await initData(); // 如果encounterId发生变化,重新加载检验申请单列表 - if (oldEncounterId !== newVal.encounterId) { + if (oldEncounterId !== newEncounterId) { getInspectionList() } - - // 更新科室编码 - // const currentDeptCode = await getCurrentDeptCode(); - // formData.applyDeptCode = currentDeptCode || ''; } -}, { deep: true, immediate: true }) +}, { immediate: true }) // Bug #329: 监听已选择的检验项目,自动更新检验项目文本并设置默认执行科室 watch(() => selectedInspectionItems.value, async (newVal) => { diff --git a/healthlink-his-ui/src/views/emergency/observation/index.vue b/healthlink-his-ui/src/views/emergency/observation/index.vue index 6383f2307..9ca0a44ac 100644 --- a/healthlink-his-ui/src/views/emergency/observation/index.vue +++ b/healthlink-his-ui/src/views/emergency/observation/index.vue @@ -104,7 +104,7 @@ :type="row.disposition==='ADMIT'?'warning':row.disposition==='DEATH'?'danger':'success'" size="small" > - {{ {$t('emergency.observation.dispAdmit'):t('emergency.observation.dispAdmit'),$t('emergency.observation.dispDischarge'):t('emergency.observation.dispDischarge'),$t('emergency.observation.dispTransfer'):t('emergency.observation.dispTransfer'),$t('emergency.observation.dispDeath'):t('emergency.observation.dispDeath')}[row.disposition]||row.disposition }} + {{ { ADMIT: $t('emergency.observation.dispAdmit'), DISCHARGE: $t('emergency.observation.dispDischarge'), TRANSFER: $t('emergency.observation.dispTransfer'), DEATH: $t('emergency.observation.dispDeath') }[row.disposition] || row.disposition }} - {{ {$t('emergency.triage.statusWaiting'):'待诊',$t('emergency.triage.statusInTreatment'):'就诊中',$t('emergency.triage.statusCompleted'):'已完成'}[row.status]||row.status }} + {{ { WAITING: $t('emergency.triage.statusWaiting'), IN_TREATMENT: $t('emergency.triage.statusInTreatment'), COMPLETED: $t('emergency.triage.statusCompleted') }[row.status] || row.status }} diff --git a/healthlink-his-ui/src/views/inpatientNurse/inOut/components/api.js b/healthlink-his-ui/src/views/inpatientNurse/inOut/components/api.js index 99ad25d85..3e1598dc0 100755 --- a/healthlink-his-ui/src/views/inpatientNurse/inOut/components/api.js +++ b/healthlink-his-ui/src/views/inpatientNurse/inOut/components/api.js @@ -167,6 +167,18 @@ export function cancelBedAssignment(encounterId) { }); } +// 换床/对换 +export function changeBedAssignment(encounterId, targetBedId) { + return request({ + url: '/nurse-station/atd-manage/change-bed-assignment', + method: 'put', + params: { + encounterId: encounterId, + targetBedId: targetBedId, + }, + }); +} + /** * 获取病区列表(与病区管理页面相同的接口) diff --git a/healthlink-his-ui/src/views/inpatientNurse/inOut/components/bedAllocation.vue b/healthlink-his-ui/src/views/inpatientNurse/inOut/components/bedAllocation.vue index e45509b7a..40d49b936 100755 --- a/healthlink-his-ui/src/views/inpatientNurse/inOut/components/bedAllocation.vue +++ b/healthlink-his-ui/src/views/inpatientNurse/inOut/components/bedAllocation.vue @@ -41,6 +41,14 @@ + + +