Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a4c2b6354 | ||
|
|
6626c8f1cd | ||
|
|
e2f4996f47 | ||
|
|
37b57e8b12 | ||
|
|
9bd39c06e7 | ||
|
|
bde42d6b14 | ||
|
|
01bf3177c9 | ||
|
|
2a9f8376e6 | ||
|
|
0774d9f877 | ||
|
|
cee38eceae | ||
|
|
bbc740b6ce | ||
|
|
256b986c0e | ||
|
|
eaac16769d | ||
|
|
df6c5f3824 | ||
|
|
08075c90e2 | ||
|
|
c5820fcec2 | ||
|
|
fbe7f4f41f | ||
|
|
f9ab4c5688 | ||
|
|
861129c9d4 | ||
|
|
78a2dfa3fe | ||
|
|
98e5a0b984 | ||
| e5c944069b | |||
| a68ffbfec4 | |||
| 14f8a8b0a3 | |||
| 0f1e57227b | |||
|
|
98a370f3a2 | ||
| b2ce368749 | |||
| c691f82958 | |||
|
|
d2bfde7230 | ||
|
|
62a09a8b94 | ||
|
|
10b2c58a3e | ||
|
|
abc995523b | ||
|
|
c0438c0288 | ||
|
|
13731c2373 | ||
|
|
d63b00fd33 | ||
|
|
c60cd6878e | ||
|
|
cf50f8968a | ||
|
|
94fac8e257 | ||
|
|
f162134156 | ||
|
|
d866d898df | ||
|
|
6725cef643 | ||
|
|
7790a64eab | ||
|
|
49fa1c9b90 | ||
|
|
e0a035204e | ||
|
|
349b0453c8 | ||
|
|
3ddd74d679 | ||
|
|
9829843b3e | ||
|
|
0d95cc1341 | ||
|
|
1dfceeaf46 | ||
|
|
1b79df4f93 | ||
|
|
f62a280dfc | ||
|
|
e5d949a740 | ||
|
|
4bd0d2dcc8 | ||
|
|
6451c308c2 | ||
|
|
772ec5537c | ||
|
|
af6494c806 | ||
|
|
2901dafe10 | ||
|
|
ffc2562aea | ||
|
|
5da537f863 | ||
|
|
facbe7cd44 | ||
|
|
283d25642a | ||
|
|
7b17a66214 |
@@ -10,7 +10,7 @@ import com.openhis.clinical.service.ITicketService;
|
|||||||
import com.openhis.web.appointmentmanage.appservice.ITicketAppService;
|
import com.openhis.web.appointmentmanage.appservice.ITicketAppService;
|
||||||
import com.openhis.web.appointmentmanage.dto.TicketDto;
|
import com.openhis.web.appointmentmanage.dto.TicketDto;
|
||||||
import com.openhis.common.constant.CommonConstants.SlotStatus;
|
import com.openhis.common.constant.CommonConstants.SlotStatus;
|
||||||
import com.openhis.common.constant.CommonConstants.AppointmentOrderStatus;
|
import com.openhis.common.enums.OrderStatus;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
@@ -198,10 +198,11 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
if (SlotStatus.CHECKED_IN.equals(slotStatus)) {
|
if (SlotStatus.CHECKED_IN.equals(slotStatus)) {
|
||||||
dto.setStatus("已取号");
|
dto.setStatus("已取号");
|
||||||
} else if (SlotStatus.BOOKED.equals(slotStatus)) {
|
} else if (SlotStatus.BOOKED.equals(slotStatus)) {
|
||||||
if (AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus())) {
|
// order_main.status: 0=患者取消(已退号) 2=系统取消 其余=已预约
|
||||||
dto.setStatus("已取号");
|
if (OrderStatus.PATIENT_CANCELLED.getValue().equals(raw.getOrderStatus())) {
|
||||||
} else if (AppointmentOrderStatus.RETURNED.equals(raw.getOrderStatus())) {
|
|
||||||
dto.setStatus("已退号");
|
dto.setStatus("已退号");
|
||||||
|
} else if (OrderStatus.SYSTEM_CANCELLED.getValue().equals(raw.getOrderStatus())) {
|
||||||
|
dto.setStatus("系统取消");
|
||||||
} else {
|
} else {
|
||||||
dto.setStatus("已预约");
|
dto.setStatus("已预约");
|
||||||
}
|
}
|
||||||
@@ -372,10 +373,11 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
if (SlotStatus.CHECKED_IN.equals(slotStatus)) {
|
if (SlotStatus.CHECKED_IN.equals(slotStatus)) {
|
||||||
dto.setStatus("已取号");
|
dto.setStatus("已取号");
|
||||||
} else if (SlotStatus.BOOKED.equals(slotStatus)) {
|
} else if (SlotStatus.BOOKED.equals(slotStatus)) {
|
||||||
if (AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus())) {
|
// order_main.status: 0=患者取消(已退号) 2=系统取消 其余=已预约
|
||||||
dto.setStatus("已取号");
|
if (OrderStatus.PATIENT_CANCELLED.getValue().equals(raw.getOrderStatus())) {
|
||||||
} else if (AppointmentOrderStatus.RETURNED.equals(raw.getOrderStatus())) {
|
|
||||||
dto.setStatus("已退号");
|
dto.setStatus("已退号");
|
||||||
|
} else if (OrderStatus.SYSTEM_CANCELLED.getValue().equals(raw.getOrderStatus())) {
|
||||||
|
dto.setStatus("系统取消");
|
||||||
} else {
|
} else {
|
||||||
dto.setStatus("已预约");
|
dto.setStatus("已预约");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,8 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation
|
|||||||
BeanUtils.copyProperties(orgLocQueryDto, orgLoc);
|
BeanUtils.copyProperties(orgLocQueryDto, orgLoc);
|
||||||
|
|
||||||
Long activityDefinitionId = orgLoc.getActivityDefinitionId();
|
Long activityDefinitionId = orgLoc.getActivityDefinitionId();
|
||||||
String activityName = activityDefinitionId != null ? activityDefinitionMapper.selectById(activityDefinitionId).getName() : "";
|
String activityName = activityDefinitionId != null
|
||||||
|
? activityDefinitionMapper.selectById(activityDefinitionId).getName() : "";
|
||||||
|
|
||||||
List<OrganizationLocation> organizationLocationList =
|
List<OrganizationLocation> organizationLocationList =
|
||||||
organizationLocationService.getOrgLocListByOrgIdAndActivityDefinitionId(orgLoc.getActivityDefinitionId());
|
organizationLocationService.getOrgLocListByOrgIdAndActivityDefinitionId(orgLoc.getActivityDefinitionId());
|
||||||
@@ -147,8 +148,8 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation
|
|||||||
for (OrganizationLocation organizationLocation : organizationLocationList)
|
for (OrganizationLocation organizationLocation : organizationLocationList)
|
||||||
if (DateTimeUtils.isOverlap(organizationLocation.getStartTime(), organizationLocation.getEndTime(),
|
if (DateTimeUtils.isOverlap(organizationLocation.getStartTime(), organizationLocation.getEndTime(),
|
||||||
orgLoc.getStartTime(), orgLoc.getEndTime())) {
|
orgLoc.getStartTime(), orgLoc.getEndTime())) {
|
||||||
String organizationName =
|
Organization org = organizationService.getById(organizationLocation.getOrganizationId());
|
||||||
organizationService.getById(organizationLocation.getOrganizationId()).getName();
|
String organizationName = org != null ? org.getName() : "未知科室";
|
||||||
return R.fail("当前诊疗:" + activityName + CommonConstants.Common.DASH + orgLoc.getStartTime()
|
return R.fail("当前诊疗:" + activityName + CommonConstants.Common.DASH + orgLoc.getStartTime()
|
||||||
+ CommonConstants.Common.DASH + orgLoc.getEndTime() + "与" + organizationName + "时间冲突");
|
+ CommonConstants.Common.DASH + orgLoc.getEndTime() + "与" + organizationName + "时间冲突");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.openhis.web.chargemanage.appservice.impl;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
@@ -329,16 +330,14 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果本次门诊挂号来自预约签到,同步把预约订单与号源槽位状态改为已退号
|
// 退费成功后,同步回滚预约订单状态及号源;同时移除分诊队列
|
||||||
|
Long refundOrderMainId = null;
|
||||||
if (result != null && result.getCode() == 200) {
|
if (result != null && result.getCode() == 200) {
|
||||||
syncAppointmentReturnStatus(byId, cancelRegPaymentDto.getReason());
|
refundOrderMainId = syncAppointmentReturnStatus(byId, cancelRegPaymentDto.getReason());
|
||||||
|
|
||||||
// 同步移除分诊队列中的记录
|
|
||||||
removeTriageQueueItem(byId.getId());
|
removeTriageQueueItem(byId.getId());
|
||||||
}
|
}
|
||||||
|
// 退号日志独立事务写入,无论退费成功与否均记录
|
||||||
// 记录退号日志
|
recordRefundLog(cancelRegPaymentDto, byId, result, paymentRecon, refundOrderMainId);
|
||||||
recordRefundLog(cancelRegPaymentDto, byId, result, paymentRecon);
|
|
||||||
|
|
||||||
// 2025/05/05 该处保存费用项后,会通过统一收费处理进行收费
|
// 2025/05/05 该处保存费用项后,会通过统一收费处理进行收费
|
||||||
return R.ok(paymentRecon, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[] {"退号"}));
|
return R.ok(paymentRecon, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[] {"退号"}));
|
||||||
@@ -435,8 +434,6 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
// 通过患者、科室、日期查找关联的预约订单
|
// 通过患者、科室、日期查找关联的预约订单
|
||||||
LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<Order>()
|
LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<Order>()
|
||||||
.eq(Order::getPatientId, encounter.getPatientId())
|
.eq(Order::getPatientId, encounter.getPatientId())
|
||||||
.in(Order::getStatus, CommonConstants.AppointmentOrderStatus.BOOKED,
|
|
||||||
CommonConstants.AppointmentOrderStatus.CHECKED_IN)
|
|
||||||
.orderByDesc(Order::getUpdateTime)
|
.orderByDesc(Order::getUpdateTime)
|
||||||
.orderByDesc(Order::getCreateTime)
|
.orderByDesc(Order::getCreateTime)
|
||||||
.last("LIMIT 1");
|
.last("LIMIT 1");
|
||||||
@@ -590,20 +587,25 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 同步预约号源状态为已退号。
|
* 诊前退号:回滚预约订单、号源槽位、号源池统计。
|
||||||
* 说明:
|
*
|
||||||
* 1) 门诊退号主流程不依赖该步骤成功与否,因此此方法内部异常仅记录日志,不向上抛出。
|
* <p>处理四件事:
|
||||||
* 2) 通过患者、科室、日期以及状态筛选最近一条预约订单,尽量避免误匹配。
|
* <ol>
|
||||||
|
* <li>order_main → status=0(患者取消), pay_status=3(已退费), 写入取消时间和原因</li>
|
||||||
|
* <li>adm_schedule_slot → status=0(待约), order_id=NULL(释放号源)</li>
|
||||||
|
* <li>adm_schedule_pool → 重算统计值 + version+1</li>
|
||||||
|
* <li>返回 order_main.id 供 refund_log 关联</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* <p>异常仅记录日志不向上抛,不影响主流程返回成功。
|
||||||
*/
|
*/
|
||||||
private void syncAppointmentReturnStatus(Encounter encounter, String reason) {
|
private Long syncAppointmentReturnStatus(Encounter encounter, String reason) {
|
||||||
if (encounter == null || encounter.getPatientId() == null) {
|
if (encounter == null || encounter.getPatientId() == null) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<Order>()
|
LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<Order>()
|
||||||
.eq(Order::getPatientId, encounter.getPatientId())
|
.eq(Order::getPatientId, encounter.getPatientId())
|
||||||
.in(Order::getStatus, CommonConstants.AppointmentOrderStatus.BOOKED,
|
|
||||||
CommonConstants.AppointmentOrderStatus.CHECKED_IN)
|
|
||||||
.orderByDesc(Order::getUpdateTime)
|
.orderByDesc(Order::getUpdateTime)
|
||||||
.orderByDesc(Order::getCreateTime)
|
.orderByDesc(Order::getCreateTime)
|
||||||
.last("LIMIT 1");
|
.last("LIMIT 1");
|
||||||
@@ -625,35 +627,55 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
|
|
||||||
Order appointmentOrder = orderService.getOne(queryWrapper, false);
|
Order appointmentOrder = orderService.getOne(queryWrapper, false);
|
||||||
if (appointmentOrder == null) {
|
if (appointmentOrder == null) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Date now = new Date();
|
// 只有有效订单(1)才能退号
|
||||||
if (!CommonConstants.AppointmentOrderStatus.RETURNED.equals(appointmentOrder.getStatus())) {
|
if (!OrderStatus.ACTIVE.getValue().equals(appointmentOrder.getStatus())) {
|
||||||
Order updateOrder = new Order();
|
log.warn("退号跳过:订单状态非有效, orderId={}, status={}",
|
||||||
updateOrder.setId(appointmentOrder.getId());
|
appointmentOrder.getId(), appointmentOrder.getStatus());
|
||||||
updateOrder.setStatus(CommonConstants.AppointmentOrderStatus.RETURNED);
|
return null;
|
||||||
updateOrder.setCancelTime(now);
|
}
|
||||||
updateOrder.setCancelReason(
|
|
||||||
StringUtils.isNotEmpty(reason) ? reason : "门诊退号");
|
// 乐观锁更新:WHERE version = 旧值,防并发重复退号
|
||||||
updateOrder.setUpdateTime(now);
|
boolean updated = orderService.update(
|
||||||
orderService.updateById(updateOrder);
|
new LambdaUpdateWrapper<Order>()
|
||||||
|
.set(Order::getStatus, OrderStatus.PATIENT_CANCELLED.getValue())
|
||||||
|
.set(Order::getPayStatus, PaymentStatus.REFUND_ALL.getValue())
|
||||||
|
.set(Order::getCancelTime, new Date())
|
||||||
|
.set(Order::getCancelReason,
|
||||||
|
StringUtils.isNotEmpty(reason) ? reason : "诊前退号")
|
||||||
|
.set(Order::getUpdateTime, new Date())
|
||||||
|
.setSql("version = version + 1")
|
||||||
|
.eq(Order::getId, appointmentOrder.getId())
|
||||||
|
.eq(Order::getVersion, appointmentOrder.getVersion())
|
||||||
|
);
|
||||||
|
if (!updated) {
|
||||||
|
log.warn("退号乐观锁冲突,订单已被其他操作修改, orderId={}", appointmentOrder.getId());
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Long slotId = appointmentOrder.getSlotId();
|
Long slotId = appointmentOrder.getSlotId();
|
||||||
if (slotId == null) {
|
if (slotId == null) {
|
||||||
return;
|
return appointmentOrder.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
int slotRows = scheduleSlotMapper.updateSlotStatus(slotId, CommonConstants.SlotStatus.RETURNED);
|
int slotRows = scheduleSlotMapper.updateSlotStatus(slotId, CommonConstants.SlotStatus.AVAILABLE);
|
||||||
if (slotRows > 0) {
|
if (slotRows > 0) {
|
||||||
Long poolId = scheduleSlotMapper.selectPoolIdBySlotId(slotId);
|
Long poolId = scheduleSlotMapper.selectPoolIdBySlotId(slotId);
|
||||||
if (poolId != null) {
|
if (poolId != null) {
|
||||||
schedulePoolMapper.refreshPoolStats(poolId);
|
schedulePoolMapper.refreshPoolStats(poolId);
|
||||||
|
schedulePoolMapper.update(null,
|
||||||
|
new LambdaUpdateWrapper<SchedulePool>()
|
||||||
|
.setSql("version = version + 1")
|
||||||
|
.set(SchedulePool::getUpdateTime, new Date())
|
||||||
|
.eq(SchedulePool::getId, poolId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return appointmentOrder.getId();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("同步预约号源已退号状态失败, encounterId={}", encounter.getId(), e);
|
log.warn("同步预约号源已退号状态失败, encounterId={}", encounter.getId(), e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -672,22 +694,29 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 记录退号日志
|
* 记录退号日志(独立事务)。
|
||||||
|
*
|
||||||
|
* <p>REQUIRES_NEW 确保即使主事务回滚,退号审计日志也不丢失。
|
||||||
|
* orderMainId 优先使用 order_main.id,若退费失败则 fallback 到 encounterId。
|
||||||
*
|
*
|
||||||
* @param cancelRegPaymentDto 退号请求对象
|
* @param cancelRegPaymentDto 退号请求对象
|
||||||
* @param encounter 就诊信息
|
* @param encounter 就诊信息
|
||||||
* @param result 退号结果
|
* @param result 退号结果
|
||||||
* @param paymentRecon 支付对账信息
|
* @param paymentRecon 支付对账信息
|
||||||
|
* @param orderMainId 预约订单主键(order_main.id),用于关联业务数据
|
||||||
*/
|
*/
|
||||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||||
public void recordRefundLog(CancelRegPaymentDto cancelRegPaymentDto,
|
public void recordRefundLog(CancelRegPaymentDto cancelRegPaymentDto,
|
||||||
Encounter encounter,
|
Encounter encounter,
|
||||||
R<?> result,
|
R<?> result,
|
||||||
PaymentReconciliation paymentRecon) {
|
PaymentReconciliation paymentRecon,
|
||||||
|
Long orderMainId) {
|
||||||
RefundLog refundLog = new RefundLog();
|
RefundLog refundLog = new RefundLog();
|
||||||
try {
|
try {
|
||||||
// 1. 订单ID(唯一)
|
// 1. 订单ID(关联 order_main.id)
|
||||||
String orderId = String.valueOf(cancelRegPaymentDto.getEncounterId());
|
String orderId = orderMainId != null
|
||||||
|
? String.valueOf(orderMainId)
|
||||||
|
: String.valueOf(cancelRegPaymentDto.getEncounterId());
|
||||||
refundLog.setOrderId(orderId);
|
refundLog.setOrderId(orderId);
|
||||||
|
|
||||||
// 已存在则不重复插入(防止唯一约束异常)
|
// 已存在则不重复插入(防止唯一约束异常)
|
||||||
|
|||||||
@@ -418,7 +418,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
|||||||
// 清除相关缓存
|
// 清除相关缓存
|
||||||
clearSurgeryAppCache(surgery);
|
clearSurgeryAppCache(surgery);
|
||||||
|
|
||||||
return R.ok(surgeryId, MessageUtils.createMessage(PromptMsgConstant.Common.M00001, new Object[]{"手术信息"}));
|
return R.ok(surgeryId, "手术申请提交成功!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -497,7 +497,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
|||||||
// 清除相关缓存
|
// 清除相关缓存
|
||||||
clearSurgeryAppCache(surgery);
|
clearSurgeryAppCache(surgery);
|
||||||
|
|
||||||
return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00002, new Object[]{"手术信息"}));
|
return R.ok(null, "手术申请修改成功!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -83,9 +83,14 @@ public class AdviceUtils {
|
|||||||
* @return 提示信息
|
* @return 提示信息
|
||||||
*/
|
*/
|
||||||
public String checkInventory(List<AdviceSaveDto> adviceSaveList) {
|
public String checkInventory(List<AdviceSaveDto> adviceSaveList) {
|
||||||
// 医嘱定义ID集合
|
// 医嘱定义ID集合(过滤null值,避免SQL查询异常)
|
||||||
List<Long> adviceDefinitionIdList
|
List<Long> adviceDefinitionIdList
|
||||||
= adviceSaveList.stream().map(AdviceSaveDto::getAdviceDefinitionId).collect(Collectors.toList());
|
= adviceSaveList.stream().map(AdviceSaveDto::getAdviceDefinitionId)
|
||||||
|
.filter(id -> id != null).collect(Collectors.toList());
|
||||||
|
// 🔧 Bug #504 修复:如果所有adviceDefinitionId都为null,跳过库存校验
|
||||||
|
if (adviceDefinitionIdList.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
// 医嘱库存集合
|
// 医嘱库存集合
|
||||||
List<AdviceInventoryDto> adviceInventoryList
|
List<AdviceInventoryDto> adviceInventoryList
|
||||||
= doctorStationAdviceAppMapper.getAdviceInventory(null, adviceDefinitionIdList,
|
= doctorStationAdviceAppMapper.getAdviceInventory(null, adviceDefinitionIdList,
|
||||||
@@ -99,6 +104,10 @@ public class AdviceUtils {
|
|||||||
= this.subtractInventory(adviceInventoryList, adviceDraftInventoryList);
|
= this.subtractInventory(adviceInventoryList, adviceDraftInventoryList);
|
||||||
// 检查库存
|
// 检查库存
|
||||||
for (AdviceSaveDto saveDto : adviceSaveList) {
|
for (AdviceSaveDto saveDto : adviceSaveList) {
|
||||||
|
// 🔧 Bug #504 修复:退回医嘱可能adviceDefinitionId为空,跳过校验
|
||||||
|
if (saveDto.getAdviceDefinitionId() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
boolean matched = false;
|
boolean matched = false;
|
||||||
for (AdviceInventoryDto inventoryDto : adviceInventory) {
|
for (AdviceInventoryDto inventoryDto : adviceInventory) {
|
||||||
// 匹配条件:adviceDefinitionId, adviceTableName, locationId, lotNumber 同时相等
|
// 匹配条件:adviceDefinitionId, adviceTableName, locationId, lotNumber 同时相等
|
||||||
@@ -108,10 +117,12 @@ public class AdviceUtils {
|
|||||||
|| saveDto.getLotNumber().equals(inventoryDto.getLotNumber());
|
|| saveDto.getLotNumber().equals(inventoryDto.getLotNumber());
|
||||||
boolean tableNameMatch = StringUtils.isEmpty(saveDto.getAdviceTableName())
|
boolean tableNameMatch = StringUtils.isEmpty(saveDto.getAdviceTableName())
|
||||||
|| inventoryDto.getItemTable().equals(saveDto.getAdviceTableName());
|
|| inventoryDto.getItemTable().equals(saveDto.getAdviceTableName());
|
||||||
// if (saveDto.)
|
// 🔧 Bug #504 修复:退回医嘱可能locationId为空,跳过location匹配
|
||||||
|
boolean locationMatch = saveDto.getLocationId() == null
|
||||||
|
|| inventoryDto.getLocationId().equals(saveDto.getLocationId());
|
||||||
if (inventoryDto.getItemId().equals(saveDto.getAdviceDefinitionId())
|
if (inventoryDto.getItemId().equals(saveDto.getAdviceDefinitionId())
|
||||||
&& tableNameMatch
|
&& tableNameMatch
|
||||||
&& inventoryDto.getLocationId().equals(saveDto.getLocationId()) && lotNumberMatch) {
|
&& locationMatch && lotNumberMatch) {
|
||||||
matched = true;
|
matched = true;
|
||||||
// 检查库存是否充足
|
// 检查库存是否充足
|
||||||
BigDecimal minUnitQuantity = saveDto.getMinUnitQuantity();
|
BigDecimal minUnitQuantity = saveDto.getMinUnitQuantity();
|
||||||
@@ -167,22 +178,26 @@ public class AdviceUtils {
|
|||||||
// 生命提示信息集合
|
// 生命提示信息集合
|
||||||
List<String> tipsList = new ArrayList<>();
|
List<String> tipsList = new ArrayList<>();
|
||||||
for (MedicationRequestUseExe medicationRequestUseExe : medUseExeList) {
|
for (MedicationRequestUseExe medicationRequestUseExe : medUseExeList) {
|
||||||
Optional<AdviceInventoryDto> matchedInventory = adviceInventory.stream()
|
// 聚合同一位置所有批次的库存总量
|
||||||
|
List<AdviceInventoryDto> matchedInventories = adviceInventory.stream()
|
||||||
.filter(inventoryDto -> medicationRequestUseExe.getMedicationId().equals(inventoryDto.getItemId())
|
.filter(inventoryDto -> medicationRequestUseExe.getMedicationId().equals(inventoryDto.getItemId())
|
||||||
&& CommonConstants.TableName.MED_MEDICATION_DEFINITION.equals(inventoryDto.getItemTable())
|
&& CommonConstants.TableName.MED_MEDICATION_DEFINITION.equals(inventoryDto.getItemTable())
|
||||||
&& medicationRequestUseExe.getPerformLocation().equals(inventoryDto.getLocationId())
|
&& medicationRequestUseExe.getPerformLocation().equals(inventoryDto.getLocationId())
|
||||||
// 如果选择了具体的批次号,校验库存时需要加上批次号的匹配条件
|
// 如果选择了具体的批次号,校验库存时需要加上批次号的匹配条件
|
||||||
&& (StringUtils.isEmpty(medicationRequestUseExe.getLotNumber())
|
&& (StringUtils.isEmpty(medicationRequestUseExe.getLotNumber())
|
||||||
|| medicationRequestUseExe.getLotNumber().equals(inventoryDto.getLotNumber())))
|
|| medicationRequestUseExe.getLotNumber().equals(inventoryDto.getLotNumber())))
|
||||||
.findFirst();
|
.collect(Collectors.toList());
|
||||||
// 匹配到库存信息
|
// 匹配到库存信息
|
||||||
if (matchedInventory.isPresent()) {
|
if (!matchedInventories.isEmpty()) {
|
||||||
AdviceInventoryDto inventoryDto = matchedInventory.get();
|
// 聚合所有批次的可用库存
|
||||||
if ((medicationRequestUseExe.getExecuteTimesNum()
|
BigDecimal totalQuantity = matchedInventories.stream()
|
||||||
.multiply(medicationRequestUseExe.getMinUnitQuantity()))
|
.map(dto -> dto.getQuantity() != null ? dto.getQuantity() : BigDecimal.ZERO)
|
||||||
.compareTo(inventoryDto.getQuantity()) > 0) {
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||||
|
BigDecimal requestQuantity = medicationRequestUseExe.getExecuteTimesNum()
|
||||||
|
.multiply(medicationRequestUseExe.getMinUnitQuantity());
|
||||||
|
if (requestQuantity.compareTo(totalQuantity) > 0) {
|
||||||
tipsList
|
tipsList
|
||||||
.add("【" + medicationRequestUseExe.getBusNo() + "】在" + inventoryDto.getLocationName() + "库存不足");
|
.add("【" + medicationRequestUseExe.getBusNo() + "】在" + matchedInventories.get(0).getLocationName() + "库存不足");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tipsList.add("【" + medicationRequestUseExe.getBusNo() + "】未匹配到库存信息");
|
tipsList.add("【" + medicationRequestUseExe.getBusNo() + "】未匹配到库存信息");
|
||||||
|
|||||||
@@ -206,6 +206,8 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
|||||||
e.setTherapyEnum_enumText(EnumUtils.getInfoByValue(TherapyTimeType.class, e.getTherapyEnum()));
|
e.setTherapyEnum_enumText(EnumUtils.getInfoByValue(TherapyTimeType.class, e.getTherapyEnum()));
|
||||||
// 请求状态
|
// 请求状态
|
||||||
e.setRequestStatus_enumText(EnumUtils.getInfoByValue(RequestStatus.class, e.getRequestStatus()));
|
e.setRequestStatus_enumText(EnumUtils.getInfoByValue(RequestStatus.class, e.getRequestStatus()));
|
||||||
|
// 发药状态
|
||||||
|
e.setDispenseStatus_enumText(EnumUtils.getInfoByValue(DispenseStatus.class, e.getDispenseStatus()));
|
||||||
// 性别枚举
|
// 性别枚举
|
||||||
e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum()));
|
e.setGenderEnum_enumText(EnumUtils.getInfoByValue(AdministrativeGender.class, e.getGenderEnum()));
|
||||||
// 计算年龄
|
// 计算年龄
|
||||||
@@ -356,6 +358,17 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
|||||||
medRequestList.add(item);
|
medRequestList.add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 校验药品医嘱是否已发药,已发药的医嘱不允许退回
|
||||||
|
if (!medRequestList.isEmpty()) {
|
||||||
|
List<Long> medReqIds = medRequestList.stream().map(PerformInfoDto::getRequestId).toList();
|
||||||
|
List<MedicationDispense> dispenseList = medicationDispenseService.list(
|
||||||
|
new LambdaQueryWrapper<MedicationDispense>()
|
||||||
|
.in(MedicationDispense::getMedReqId, medReqIds)
|
||||||
|
.eq(MedicationDispense::getStatusEnum, DispenseStatus.COMPLETED.getValue()));
|
||||||
|
if (!dispenseList.isEmpty()) {
|
||||||
|
return R.fail("该医嘱已发药,无法退回");
|
||||||
|
}
|
||||||
|
}
|
||||||
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
|
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
|
||||||
Date checkDate = new Date();
|
Date checkDate = new Date();
|
||||||
if (!serviceRequestList.isEmpty()) {
|
if (!serviceRequestList.isEmpty()) {
|
||||||
|
|||||||
@@ -255,4 +255,8 @@ public class InpatientAdviceDto {
|
|||||||
|
|
||||||
/** 总价 */
|
/** 总价 */
|
||||||
private BigDecimal totalPrice;
|
private BigDecimal totalPrice;
|
||||||
|
|
||||||
|
/** 发药状态 */
|
||||||
|
private Integer dispenseStatus;
|
||||||
|
private String dispenseStatus_enumText;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1991,7 +1991,7 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
|
|||||||
Order appointmentOrder = iOrderService.getOne(
|
Order appointmentOrder = iOrderService.getOne(
|
||||||
new LambdaQueryWrapper<Order>()
|
new LambdaQueryWrapper<Order>()
|
||||||
.eq(Order::getPatientId, encounterFormData.getPatientId())
|
.eq(Order::getPatientId, encounterFormData.getPatientId())
|
||||||
.eq(Order::getStatus, CommonConstants.AppointmentOrderStatus.CHECKED_IN)
|
.eq(Order::getStatus, OrderStatus.ACTIVE.getValue()) // 有效订单(1)
|
||||||
.eq(Order::getDeleteFlag, "0")
|
.eq(Order::getDeleteFlag, "0")
|
||||||
.orderByDesc(Order::getCreateTime)
|
.orderByDesc(Order::getCreateTime)
|
||||||
.last("LIMIT 1")
|
.last("LIMIT 1")
|
||||||
@@ -2114,11 +2114,11 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
|
|||||||
Long queuePoolId = null;
|
Long queuePoolId = null;
|
||||||
Long queueSlotId = null;
|
Long queueSlotId = null;
|
||||||
try {
|
try {
|
||||||
// 查询患者当天的待签到预约订单(status = 1 或 2 表示已预约或已取号)
|
// 查询患者当天有效订单(1);已取消(0/2)和已完成(3)的不参与排队
|
||||||
Order order = iOrderService.getOne(
|
Order order = iOrderService.getOne(
|
||||||
new LambdaQueryWrapper<Order>()
|
new LambdaQueryWrapper<Order>()
|
||||||
.eq(Order::getPatientId, encounter.getPatientId())
|
.eq(Order::getPatientId, encounter.getPatientId())
|
||||||
.in(Order::getStatus, 1, 2) // 1=BOOKED 已预约, 2=CHECKED_IN 已取号
|
.eq(Order::getStatus, OrderStatus.ACTIVE.getValue()) // 有效(1)
|
||||||
.eq(Order::getDeleteFlag, "0")
|
.eq(Order::getDeleteFlag, "0")
|
||||||
.orderByDesc(Order::getCreateTime)
|
.orderByDesc(Order::getCreateTime)
|
||||||
.last("LIMIT 1")
|
.last("LIMIT 1")
|
||||||
|
|||||||
@@ -511,6 +511,9 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
|||||||
// 签发操作
|
// 签发操作
|
||||||
boolean is_sign = AdviceOpType.SIGN_ADVICE.getCode().equals(adviceOpType);
|
boolean is_sign = AdviceOpType.SIGN_ADVICE.getCode().equals(adviceOpType);
|
||||||
|
|
||||||
|
// 收集已处理的requestId,用于批量更新状态
|
||||||
|
List<Long> processedRequestIds = new ArrayList<>();
|
||||||
|
|
||||||
// 声明长期医嘱诊疗请求
|
// 声明长期医嘱诊疗请求
|
||||||
ServiceRequest longServiceRequest;
|
ServiceRequest longServiceRequest;
|
||||||
// 新增 + 修改 (长期医嘱)
|
// 新增 + 修改 (长期医嘱)
|
||||||
@@ -555,6 +558,9 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
iServiceRequestService.saveOrUpdate(longServiceRequest);
|
iServiceRequestService.saveOrUpdate(longServiceRequest);
|
||||||
|
if (longServiceRequest.getId() != null) {
|
||||||
|
processedRequestIds.add(longServiceRequest.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 声明临时医嘱诊疗请求
|
// 声明临时医嘱诊疗请求
|
||||||
@@ -603,6 +609,9 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
iServiceRequestService.saveOrUpdate(tempServiceRequest);
|
iServiceRequestService.saveOrUpdate(tempServiceRequest);
|
||||||
|
if (tempServiceRequest.getId() != null) {
|
||||||
|
processedRequestIds.add(tempServiceRequest.getId());
|
||||||
|
}
|
||||||
|
|
||||||
// 保存时,保存诊疗费用项
|
// 保存时,保存诊疗费用项
|
||||||
if (is_save) {
|
if (is_save) {
|
||||||
@@ -654,6 +663,14 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 批量更新诊疗医嘱状态(使用 update 确保状态字段必定更新)
|
||||||
|
if (!processedRequestIds.isEmpty()) {
|
||||||
|
iServiceRequestService.update(null,
|
||||||
|
new LambdaUpdateWrapper<ServiceRequest>()
|
||||||
|
.set(ServiceRequest::getStatusEnum,
|
||||||
|
is_save ? RequestStatus.DRAFT.getValue() : RequestStatus.ACTIVE.getValue())
|
||||||
|
.in(ServiceRequest::getId, processedRequestIds));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -434,7 +434,7 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
|||||||
return new java.util.ArrayList<>(); // 返回空列表而不是查询数据库
|
return new java.util.ArrayList<>(); // 返回空列表而不是查询数据库
|
||||||
}
|
}
|
||||||
|
|
||||||
List<RequestFormQueryDto> requestFormList = requestFormManageAppMapper.getRequestForm(encounterId, typeCode, startDate, endDate, status);
|
List<RequestFormQueryDto> requestFormList = requestFormManageAppMapper.getRequestForm(encounterId, typeCode, startDate, endDate, status, null);
|
||||||
for (RequestFormQueryDto requestFormQueryDto : requestFormList) {
|
for (RequestFormQueryDto requestFormQueryDto : requestFormList) {
|
||||||
// 查询处方详情
|
// 查询处方详情
|
||||||
List<RequestFormDetailQueryDto> requestFormDetail =
|
List<RequestFormDetailQueryDto> requestFormDetail =
|
||||||
|
|||||||
@@ -37,13 +37,15 @@ public interface RequestFormManageAppMapper {
|
|||||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||||
* @param status 单据状态(可选)
|
* @param status 单据状态(可选)
|
||||||
|
* @param keyword 关键字(可选,申请单号/检查项目名称模糊匹配)
|
||||||
* @return 申请单列表
|
* @return 申请单列表
|
||||||
*/
|
*/
|
||||||
List<RequestFormQueryDto> getRequestForm(@Param("encounterId") Long encounterId,
|
List<RequestFormQueryDto> getRequestForm(@Param("encounterId") Long encounterId,
|
||||||
@Param("typeCode") String typeCode,
|
@Param("typeCode") String typeCode,
|
||||||
@Param("startDate") String startDate,
|
@Param("startDate") String startDate,
|
||||||
@Param("endDate") String endDate,
|
@Param("endDate") String endDate,
|
||||||
@Param("status") String status);
|
@Param("status") String status,
|
||||||
|
@Param("keyword") String keyword);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询申请单详情
|
* 查询申请单详情
|
||||||
|
|||||||
@@ -331,12 +331,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
) t
|
) t
|
||||||
WHERE rn = 1
|
WHERE rn = 1
|
||||||
) pi ON s.patient_id = pi.patient_id
|
) pi ON s.patient_id = pi.patient_id
|
||||||
<!-- 排除已生成医嘱的手术 -->
|
<!-- 关联服务请求表(仅用于数据关联,不再过滤) -->
|
||||||
LEFT JOIN wor_service_request sr ON sr.activity_id = s.id AND sr.delete_flag = '0' AND sr.category_enum = 4
|
LEFT JOIN wor_service_request sr ON sr.activity_id = s.id AND sr.delete_flag = '0' AND sr.category_enum = 4
|
||||||
<where>
|
<where>
|
||||||
s.delete_flag = '0'
|
s.delete_flag = '0'
|
||||||
<!-- 只显示未生成医嘱的手术 -->
|
|
||||||
AND sr.id IS NULL
|
|
||||||
<if test="ew.sqlSegment != null and ew.sqlSegment != ''">
|
<if test="ew.sqlSegment != null and ew.sqlSegment != ''">
|
||||||
<!-- 补充 encounter_id 替换,修复多表关联时字段歧义。注释不能放进 OGNL 表达式内部。 -->
|
<!-- 补充 encounter_id 替换,修复多表关联时字段歧义。注释不能放进 OGNL 表达式内部。 -->
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
|
|||||||
@@ -153,7 +153,8 @@
|
|||||||
ii.balance_amount AS balance_amount,
|
ii.balance_amount AS balance_amount,
|
||||||
ii.account_id AS account_id,
|
ii.account_id AS account_id,
|
||||||
ii.performer_check_id,
|
ii.performer_check_id,
|
||||||
ii.category_code
|
ii.category_code,
|
||||||
|
ii.dispense_status
|
||||||
FROM (( SELECT T1.encounter_id,
|
FROM (( SELECT T1.encounter_id,
|
||||||
T1.tenant_id,
|
T1.tenant_id,
|
||||||
#{medMedicationRequest} AS advice_table,
|
#{medMedicationRequest} AS advice_table,
|
||||||
@@ -197,7 +198,8 @@
|
|||||||
pra."name" AS admitting_doctor_name,
|
pra."name" AS admitting_doctor_name,
|
||||||
personal_account.balance_amount,
|
personal_account.balance_amount,
|
||||||
personal_account.id AS account_id,
|
personal_account.id AS account_id,
|
||||||
T2.category_code
|
T2.category_code,
|
||||||
|
mmd.status_enum AS dispense_status
|
||||||
FROM med_medication_request AS T1
|
FROM med_medication_request AS T1
|
||||||
LEFT JOIN med_medication_definition AS T2
|
LEFT JOIN med_medication_definition AS T2
|
||||||
ON T2.id = T1.medication_id
|
ON T2.id = T1.medication_id
|
||||||
@@ -278,6 +280,9 @@
|
|||||||
aa.balance_amount
|
aa.balance_amount
|
||||||
) AS personal_account
|
) AS personal_account
|
||||||
ON personal_account.encounter_id = ae.id
|
ON personal_account.encounter_id = ae.id
|
||||||
|
LEFT JOIN med_medication_dispense mmd
|
||||||
|
ON mmd.med_req_id = T1.id
|
||||||
|
AND mmd.delete_flag = '0'
|
||||||
WHERE T1.delete_flag = '0'
|
WHERE T1.delete_flag = '0'
|
||||||
AND T1.refund_medicine_id IS NULL
|
AND T1.refund_medicine_id IS NULL
|
||||||
AND T1.generate_source_enum = #{doctorPrescription}
|
AND T1.generate_source_enum = #{doctorPrescription}
|
||||||
@@ -331,7 +336,8 @@
|
|||||||
pra."name" AS admitting_doctor_name,
|
pra."name" AS admitting_doctor_name,
|
||||||
personal_account.balance_amount,
|
personal_account.balance_amount,
|
||||||
personal_account.id AS account_id,
|
personal_account.id AS account_id,
|
||||||
T2.category_code
|
T2.category_code,
|
||||||
|
NULL AS dispense_status
|
||||||
FROM wor_service_request AS T1
|
FROM wor_service_request AS T1
|
||||||
LEFT JOIN wor_activity_definition AS T2
|
LEFT JOIN wor_activity_definition AS T2
|
||||||
ON T2.id = T1.activity_id
|
ON T2.id = T1.activity_id
|
||||||
|
|||||||
@@ -105,6 +105,8 @@
|
|||||||
<if test="statusEnum == 4">
|
<if test="statusEnum == 4">
|
||||||
T4.status_enum = #{completed}
|
T4.status_enum = #{completed}
|
||||||
</if>
|
</if>
|
||||||
|
AND T4.summary_no IS NOT NULL
|
||||||
|
AND T4.summary_no != ''
|
||||||
) AS ii
|
) AS ii
|
||||||
${ew.customSqlSegment}
|
${ew.customSqlSegment}
|
||||||
GROUP BY ii.encounter_id,
|
GROUP BY ii.encounter_id,
|
||||||
@@ -263,6 +265,8 @@
|
|||||||
AND T15.delete_flag = '0'
|
AND T15.delete_flag = '0'
|
||||||
WHERE T1.delete_flag = '0'
|
WHERE T1.delete_flag = '0'
|
||||||
-- 因发药配药合并,前台只能看到待发药,已发药状态,但是后台配药发药状态都查
|
-- 因发药配药合并,前台只能看到待发药,已发药状态,但是后台配药发药状态都查
|
||||||
|
AND T1.summary_no IS NOT NULL
|
||||||
|
AND T1.summary_no != ''
|
||||||
AND
|
AND
|
||||||
<if test="dispenseStatus == null">
|
<if test="dispenseStatus == null">
|
||||||
T1.status_enum IN (#{inProgress},#{completed},#{preparation},#{prepared})
|
T1.status_enum IN (#{inProgress},#{completed},#{preparation},#{prepared})
|
||||||
|
|||||||
@@ -216,7 +216,8 @@
|
|||||||
ccd.name AS condition_definition_name,
|
ccd.name AS condition_definition_name,
|
||||||
T1.therapy_enum AS therapyEnum,
|
T1.therapy_enum AS therapyEnum,
|
||||||
T1.sort_number AS sort_number,
|
T1.sort_number AS sort_number,
|
||||||
T1.based_on_id AS based_on_id
|
T1.based_on_id AS based_on_id,
|
||||||
|
T1.medication_id AS advice_definition_id
|
||||||
FROM med_medication_request AS T1
|
FROM med_medication_request AS T1
|
||||||
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
|
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
|
||||||
AND T2.delete_flag = '0'
|
AND T2.delete_flag = '0'
|
||||||
@@ -268,7 +269,8 @@
|
|||||||
'' AS condition_definition_name,
|
'' AS condition_definition_name,
|
||||||
2 AS therapyEnum,
|
2 AS therapyEnum,
|
||||||
99 AS sort_number,
|
99 AS sort_number,
|
||||||
T1.based_on_id AS based_on_id
|
T1.based_on_id AS based_on_id,
|
||||||
|
T1.device_def_id AS advice_definition_id
|
||||||
FROM wor_device_request AS T1
|
FROM wor_device_request AS T1
|
||||||
LEFT JOIN adm_device_definition AS T2 ON T2.ID = T1.device_def_id
|
LEFT JOIN adm_device_definition AS T2 ON T2.ID = T1.device_def_id
|
||||||
AND T2.delete_flag = '0'
|
AND T2.delete_flag = '0'
|
||||||
@@ -317,7 +319,8 @@
|
|||||||
'' AS condition_definition_name,
|
'' AS condition_definition_name,
|
||||||
COALESCE(T1.therapy_enum, 2) AS therapyEnum,
|
COALESCE(T1.therapy_enum, 2) AS therapyEnum,
|
||||||
99 AS sort_number,
|
99 AS sort_number,
|
||||||
T1.based_on_id AS based_on_id
|
T1.based_on_id AS based_on_id,
|
||||||
|
T1.activity_id AS advice_definition_id
|
||||||
FROM wor_service_request AS T1
|
FROM wor_service_request AS T1
|
||||||
LEFT JOIN wor_activity_definition AS T2
|
LEFT JOIN wor_activity_definition AS T2
|
||||||
ON T2.ID = T1.activity_id
|
ON T2.ID = T1.activity_id
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright ©2023 CJB-CNIT Team. All rights reserved
|
||||||
|
*/
|
||||||
|
package com.openhis.common.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单状态 (order_main.status)
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* 状态流转:
|
||||||
|
* 创建订单 → ACTIVE(1)
|
||||||
|
* 签到 → ACTIVE(1) 不变
|
||||||
|
* 患者退号 → PATIENT_CANCELLED(0)
|
||||||
|
* 系统取消 → SYSTEM_CANCELLED(2)
|
||||||
|
* 就诊完成 → COMPLETED(3)
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @author wangjian963
|
||||||
|
* @date 2026-05-09
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum OrderStatus implements HisEnumInterface {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 患者取消
|
||||||
|
*/
|
||||||
|
PATIENT_CANCELLED(0, "0", "患者取消"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 有效
|
||||||
|
*/
|
||||||
|
ACTIVE(1, "1", "有效"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统取消
|
||||||
|
*/
|
||||||
|
SYSTEM_CANCELLED(2, "2", "系统取消"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已完成
|
||||||
|
*/
|
||||||
|
COMPLETED(3, "3", "已完成");
|
||||||
|
|
||||||
|
private Integer value;
|
||||||
|
private String code;
|
||||||
|
private String info;
|
||||||
|
|
||||||
|
public static OrderStatus getByValue(Integer value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (OrderStatus val : values()) {
|
||||||
|
if (val.getValue().equals(value)) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,8 +6,8 @@ import com.core.common.utils.AssignSeqUtil;
|
|||||||
import com.openhis.clinical.domain.Order;
|
import com.openhis.clinical.domain.Order;
|
||||||
import com.openhis.clinical.mapper.OrderMapper;
|
import com.openhis.clinical.mapper.OrderMapper;
|
||||||
import com.openhis.clinical.service.IOrderService;
|
import com.openhis.clinical.service.IOrderService;
|
||||||
import com.openhis.common.constant.CommonConstants.AppointmentOrderStatus;
|
|
||||||
import com.openhis.common.enums.AssignSeqEnum;
|
import com.openhis.common.enums.AssignSeqEnum;
|
||||||
|
import com.openhis.common.enums.OrderStatus;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
@@ -124,7 +124,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
: new Date(); // 兜底:正常业务不应走到这里
|
: new Date(); // 兜底:正常业务不应走到这里
|
||||||
order.setAppointmentDate(appointmentDateTime);
|
order.setAppointmentDate(appointmentDateTime);
|
||||||
order.setAppointmentTime(appointmentDateTime);
|
order.setAppointmentTime(appointmentDateTime);
|
||||||
order.setStatus(AppointmentOrderStatus.BOOKED);
|
// 订单状态: 0=患者取消 1=有效 2=系统取消 3=已完成
|
||||||
|
order.setStatus(OrderStatus.ACTIVE.getValue());
|
||||||
order.setPayStatus(0);
|
order.setPayStatus(0);
|
||||||
order.setVersion(0);
|
order.setVersion(0);
|
||||||
|
|
||||||
@@ -169,10 +170,13 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
if (order == null) {
|
if (order == null) {
|
||||||
throw new RuntimeException("订单不存在");
|
throw new RuntimeException("订单不存在");
|
||||||
}
|
}
|
||||||
if (AppointmentOrderStatus.CANCELLED.equals(order.getStatus())) {
|
// 已取消(患者取消0 或 系统取消2)不可再次取消
|
||||||
|
if (OrderStatus.PATIENT_CANCELLED.getValue().equals(order.getStatus())
|
||||||
|
|| OrderStatus.SYSTEM_CANCELLED.getValue().equals(order.getStatus())) {
|
||||||
throw new RuntimeException("订单已取消");
|
throw new RuntimeException("订单已取消");
|
||||||
}
|
}
|
||||||
if (AppointmentOrderStatus.CHECKED_IN.equals(order.getStatus())) {
|
// 已完成(3)的订单不可取消
|
||||||
|
if (OrderStatus.COMPLETED.getValue().equals(order.getStatus())) {
|
||||||
throw new RuntimeException("订单已完成,无法取消");
|
throw new RuntimeException("订单已完成,无法取消");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,6 +193,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
|||||||
.eq(Order::getPatientId, patientId)
|
.eq(Order::getPatientId, patientId)
|
||||||
.eq(Order::getTenantId, tenantId)
|
.eq(Order::getTenantId, tenantId)
|
||||||
.ge(Order::getCancelTime, startTime)
|
.ge(Order::getCancelTime, startTime)
|
||||||
.eq(Order::getStatus, AppointmentOrderStatus.CANCELLED));
|
// 只统计患者主动取消(0),不含系统取消(2)
|
||||||
|
.eq(Order::getStatus, OrderStatus.PATIENT_CANCELLED.getValue()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import com.openhis.clinical.domain.Ticket;
|
|||||||
import com.openhis.clinical.mapper.TicketMapper;
|
import com.openhis.clinical.mapper.TicketMapper;
|
||||||
import com.openhis.clinical.service.IOrderService;
|
import com.openhis.clinical.service.IOrderService;
|
||||||
import com.openhis.clinical.service.ITicketService;
|
import com.openhis.clinical.service.ITicketService;
|
||||||
import com.openhis.common.constant.CommonConstants.AppointmentOrderStatus;
|
|
||||||
import com.openhis.common.constant.CommonConstants.SlotStatus;
|
import com.openhis.common.constant.CommonConstants.SlotStatus;
|
||||||
|
import com.openhis.common.enums.OrderStatus;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -195,8 +195,8 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
Date startTime = Date.from(periodStart.atZone(ZoneId.systemDefault()).toInstant());
|
Date startTime = Date.from(periodStart.atZone(ZoneId.systemDefault()).toInstant());
|
||||||
Date endTime = Date.from(periodEnd.atZone(ZoneId.systemDefault()).toInstant());
|
Date endTime = Date.from(periodEnd.atZone(ZoneId.systemDefault()).toInstant());
|
||||||
|
|
||||||
// 预约去重以订单为准(order_main),因为预约成功会先落订单;clinical_ticket 不一定在此链路写入
|
// 预约去重以订单为准(order_main),有效订单(1)才参与去重
|
||||||
List<Integer> effectiveOrderStatuses = Arrays.asList(AppointmentOrderStatus.BOOKED, AppointmentOrderStatus.CHECKED_IN);
|
List<Integer> effectiveOrderStatuses = Arrays.asList(OrderStatus.ACTIVE.getValue());
|
||||||
int exists = orderMapper.countPatientDeptOrdersInPeriod(dto.getPatientId(), slot.getDepartmentId(), slot.getDepartmentName(),
|
int exists = orderMapper.countPatientDeptOrdersInPeriod(dto.getPatientId(), slot.getDepartmentId(), slot.getDepartmentName(),
|
||||||
startTime, endTime, effectiveOrderStatuses);
|
startTime, endTime, effectiveOrderStatuses);
|
||||||
if (exists > 0) {
|
if (exists > 0) {
|
||||||
@@ -314,9 +314,8 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
|||||||
}
|
}
|
||||||
Order latestOrder = orders.get(0);
|
Order latestOrder = orders.get(0);
|
||||||
|
|
||||||
// 1. 更新订单状态为已取号,并更新支付状态和支付时间
|
// 1. 签到不改变订单状态(仍为有效1),更新支付状态为已支付并记录支付时间
|
||||||
orderService.updateOrderStatusById(latestOrder.getId(), AppointmentOrderStatus.CHECKED_IN);
|
orderService.updateOrderStatusById(latestOrder.getId(), OrderStatus.ACTIVE.getValue());
|
||||||
// 更新支付状态为已支付,记录支付时间
|
|
||||||
orderMapper.updatePayStatus(latestOrder.getId(), 1, new Date());
|
orderMapper.updatePayStatus(latestOrder.getId(), 1, new Date());
|
||||||
|
|
||||||
// 2. 查询号源槽位信息
|
// 2. 查询号源槽位信息
|
||||||
|
|||||||
@@ -160,11 +160,12 @@
|
|||||||
AND delete_flag = '0'
|
AND delete_flag = '0'
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<!-- status=0(待约)时清空order_id,释放号源,使退号后号源可再被预约 -->
|
||||||
<update id="updateSlotStatus">
|
<update id="updateSlotStatus">
|
||||||
UPDATE adm_schedule_slot
|
UPDATE adm_schedule_slot
|
||||||
SET
|
SET
|
||||||
status = #{status},
|
status = #{status},
|
||||||
<if test="status != null and '0'.equals(status.toString())">
|
<if test="status != null and status == 0">
|
||||||
order_id = NULL,
|
order_id = NULL,
|
||||||
</if>
|
</if>
|
||||||
update_time = now()
|
update_time = now()
|
||||||
|
|||||||
@@ -117,12 +117,14 @@
|
|||||||
</where>
|
</where>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- status=1: 只查有效订单(0=患者取消 1=有效 2=系统取消 3=已完成) -->
|
||||||
<select id="selectOrderById" resultMap="OrderResult">
|
<select id="selectOrderById" resultMap="OrderResult">
|
||||||
select * from order_main where id = #{id}
|
select * from order_main where id = #{id}
|
||||||
and status = 1
|
and status = 1
|
||||||
order by create_time desc
|
order by create_time desc
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<!-- status=1: 只查有效订单 -->
|
||||||
<select id="selectOrderBySlotId" resultMap="OrderResult">
|
<select id="selectOrderBySlotId" resultMap="OrderResult">
|
||||||
select * from order_main where slot_id = #{slotId} and status = 1
|
select * from order_main where slot_id = #{slotId} and status = 1
|
||||||
</select>
|
</select>
|
||||||
@@ -248,8 +250,9 @@
|
|||||||
update order_main set status = #{status} where id = #{id}
|
update order_main set status = #{status} where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<!-- status=0: 患者取消 (OrderStatus.PATIENT_CANCELLED) -->
|
||||||
<update id="updateOrderCancelInfoById">
|
<update id="updateOrderCancelInfoById">
|
||||||
update order_main set status = 3, cancel_time = #{cancelTime}, cancel_reason = #{cancelReason} where id = #{id}
|
update order_main set status = 0, cancel_time = #{cancelTime}, cancel_reason = #{cancelReason} where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
<update id="updatePayStatus">
|
<update id="updatePayStatus">
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ export function listAdviceTypes(query) {
|
|||||||
params: query
|
params: query
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询手术室详细
|
* 查询手术室详细
|
||||||
|
|||||||
@@ -1146,7 +1146,13 @@ function calculateTotalPrice(row, index) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({ getListInfo });
|
function closeAllPopovers() {
|
||||||
|
prescriptionList.value.forEach(row => {
|
||||||
|
row.showPopover = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ getListInfo, closeAllPopovers });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -41,43 +41,9 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<!-- 性别、出生日期、实足年龄 -->
|
<!-- 工作单位(学校) -->
|
||||||
<el-row :gutter="16" class="form-row align-bottom">
|
|
||||||
<el-col :span="6" class="form-item">
|
|
||||||
<span class="form-label required">性别</span>
|
|
||||||
<el-radio-group v-model="form.sex">
|
|
||||||
<el-radio label="男">男</el-radio>
|
|
||||||
<el-radio label="女">女</el-radio>
|
|
||||||
<el-radio label="未知">未知</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="10" class="form-item birth-date">
|
|
||||||
<span class="form-label required">出生日期</span>
|
|
||||||
<div class="date-inputs">
|
|
||||||
<el-input v-model="form.birthYear" class="underline-input small" maxlength="4" @change="calculateAge" />
|
|
||||||
<span>年</span>
|
|
||||||
<el-input v-model="form.birthMonth" class="underline-input small" maxlength="2" @change="calculateAge" />
|
|
||||||
<span>月</span>
|
|
||||||
<el-input v-model="form.birthDay" class="underline-input small" maxlength="2" @change="calculateAge" />
|
|
||||||
<span>日</span>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="8" class="form-item age-item">
|
|
||||||
<span class="form-label">或 实足年龄</span>
|
|
||||||
<div class="age-inputs">
|
|
||||||
<el-input v-model="form.age" class="underline-input small"/>
|
|
||||||
<el-select v-model="form.ageUnit" class="age-unit-select">
|
|
||||||
<el-option label="岁" value="岁" />
|
|
||||||
<el-option label="月" value="月" />
|
|
||||||
<el-option label="天" value="天" />
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<!-- 工作单位 -->
|
|
||||||
<el-row :gutter="16" class="form-row">
|
<el-row :gutter="16" class="form-row">
|
||||||
<el-col :span="24" class="form-item full-width">
|
<el-col :span="12" class="form-item">
|
||||||
<span class="form-label">工作单位(学校)</span>
|
<span class="form-label">工作单位(学校)</span>
|
||||||
<el-input v-model="form.workplace" class="underline-input" />
|
<el-input v-model="form.workplace" class="underline-input" />
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -522,7 +488,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, getCurrentInstance, watch } from 'vue';
|
import { ref, computed, getCurrentInstance, watch } from 'vue';
|
||||||
import pcas from 'china-division/dist/pcas-code.json';
|
import pcas from 'china-division/dist/pcas-code.json';
|
||||||
import { saveInfectiousDiseaseReport } from '../api';
|
import { saveInfectiousDiseaseReport, getNextCardNo } from '../api';
|
||||||
import useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
import { useDict } from '@/utils/dict';
|
import { useDict } from '@/utils/dict';
|
||||||
|
|
||||||
@@ -1237,7 +1203,7 @@ function calculateAge() {
|
|||||||
* 显示对话框并初始化表单数据
|
* 显示对话框并初始化表单数据
|
||||||
* @param {Object} diagnosisData - 诊断数据,包含已选疾病和地址信息
|
* @param {Object} diagnosisData - 诊断数据,包含已选疾病和地址信息
|
||||||
*/
|
*/
|
||||||
function show(diagnosisData) {
|
async function show(diagnosisData) {
|
||||||
dialogVisible.value = true;
|
dialogVisible.value = true;
|
||||||
|
|
||||||
// 重置地址选择器状态
|
// 重置地址选择器状态
|
||||||
@@ -1267,12 +1233,24 @@ function show(diagnosisData) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成默认卡片编号:医疗机构编码+当前年月日+4位流水
|
// 生成默认卡片编号:调用后端接口获取
|
||||||
const orgCode = userStore.fixmedinsCode || '0000';
|
const orgCode = userStore.fixmedinsCode || '0000';
|
||||||
|
let cardNo = '';
|
||||||
|
try {
|
||||||
|
const res = await getNextCardNo(orgCode);
|
||||||
|
if (res.code === 200 && res.data) {
|
||||||
|
cardNo = res.data;
|
||||||
|
} else {
|
||||||
|
cardNo = 'TEMP_' + Date.now();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('获取卡片编号失败:', err);
|
||||||
|
cardNo = 'TEMP_' + Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
form.value = {
|
form.value = {
|
||||||
// 卡片编号:单位自编,与网络直报一致
|
// 卡片编号:单位自编,与网络直报一致
|
||||||
cardNo: '',
|
cardNo: cardNo,
|
||||||
|
|
||||||
// 患者基本信息
|
// 患者基本信息
|
||||||
patName: patientInfo.patientName || patientInfo.name || '', // 患者姓名
|
patName: patientInfo.patientName || patientInfo.name || '', // 患者姓名
|
||||||
|
|||||||
@@ -314,6 +314,7 @@
|
|||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<span class="cat-title">{{ cat.categoryName }}</span>
|
<span class="cat-title">{{ cat.categoryName }}</span>
|
||||||
|
<span v-if="categoryLoadingSet.has(cat.typeId)" class="loading-dot"></span>
|
||||||
</template>
|
</template>
|
||||||
<div
|
<div
|
||||||
v-for="item in cat.items"
|
v-for="item in cat.items"
|
||||||
@@ -329,6 +330,9 @@
|
|||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
<span class="item-price">¥{{ item.price }}/{{ item.unit || "次" }}</span>
|
<span class="item-price">¥{{ item.price }}/{{ item.unit || "次" }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="categoryLoadingSet.has(cat.typeId)" class="category-loading-hint">
|
||||||
|
加载中...
|
||||||
|
</div>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
</div>
|
</div>
|
||||||
@@ -517,6 +521,8 @@ const rules = {
|
|||||||
const categoryList = ref([]); // 原始分类+项目数据
|
const categoryList = ref([]); // 原始分类+项目数据
|
||||||
const dictSearchKey = ref('');
|
const dictSearchKey = ref('');
|
||||||
const activeNames = ref(''); // 当前展开的折叠项
|
const activeNames = ref(''); // 当前展开的折叠项
|
||||||
|
const categoryLoadingSet = ref(new Set()); // Bug #500: 正在加载方法的分类集合
|
||||||
|
const isAnimating = ref(false); // Bug #500: 防止快速切换时折叠动画重叠导致抖动
|
||||||
|
|
||||||
const allMethods = ref([]);
|
const allMethods = ref([]);
|
||||||
|
|
||||||
@@ -632,9 +638,14 @@ const availableMethods = computed(() => {
|
|||||||
|
|
||||||
// 当可选方法列表改变时,如果当前选中的方法不在新列表中,则清空
|
// 当可选方法列表改变时,如果当前选中的方法不在新列表中,则清空
|
||||||
// #428: 分类展开时联动加载检查方法
|
// #428: 分类展开时联动加载检查方法
|
||||||
|
// Bug #500: 使用 categoryLoadingSet 替代 dictLoading,避免切换分类时整个区域闪烁
|
||||||
async function handleCategoryExpand(cat) {
|
async function handleCategoryExpand(cat) {
|
||||||
if (!cat || !cat.typeName) return;
|
if (!cat || !cat.typeName) return;
|
||||||
|
|
||||||
|
// 如果已加载过或正在加载中,跳过
|
||||||
|
if ((cat.methods && cat.methods.length > 0) || categoryLoadingSet.value.has(cat.typeId)) return;
|
||||||
|
|
||||||
|
categoryLoadingSet.value.add(cat.typeId);
|
||||||
try {
|
try {
|
||||||
const res = await searchCheckMethod({ checkType: cat.typeName });
|
const res = await searchCheckMethod({ checkType: cat.typeName });
|
||||||
let data = res?.data?.data || res?.data || res?.rows || res;
|
let data = res?.data?.data || res?.data || res?.rows || res;
|
||||||
@@ -655,14 +666,21 @@ async function handleCategoryExpand(cat) {
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('加载分类检查方法失败', err);
|
console.error('加载分类检查方法失败', err);
|
||||||
|
} finally {
|
||||||
|
categoryLoadingSet.value.delete(cat.typeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function handleCollapseChange(activeName) {
|
// Bug #500: 添加防抖逻辑,快速切换时跳过中间状态的动画,避免高度跳变和白屏闪烁
|
||||||
// 当折叠面板展开时,加载对应分类的检查方法
|
function handleCollapseChange(activeName) {
|
||||||
|
if (isAnimating.value) return; // 动画进行中,忽略后续点击
|
||||||
|
|
||||||
|
isAnimating.value = true;
|
||||||
|
setTimeout(() => { isAnimating.value = false; }, 300); // 与 CSS 过渡时长一致
|
||||||
|
|
||||||
if (activeName) {
|
if (activeName) {
|
||||||
const cat = filteredCategoryList.value.find(c => c.typeId == activeName);
|
const cat = filteredCategoryList.value.find(c => c.typeId == activeName);
|
||||||
if (cat && (!cat.methods || cat.methods.length === 0)) {
|
if (cat && (!cat.methods || cat.methods.length === 0)) {
|
||||||
await handleCategoryExpand(cat);
|
handleCategoryExpand(cat); // 异步加载,不 await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1295,6 +1313,7 @@ defineExpose({ getList });
|
|||||||
.collapse-scroll {
|
.collapse-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden; /* Bug #500: 防止切换时水平方向溢出导致抖动 */
|
||||||
}
|
}
|
||||||
.empty-hint {
|
.empty-hint {
|
||||||
color: #909399;
|
color: #909399;
|
||||||
@@ -1309,6 +1328,20 @@ defineExpose({ getList });
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #303133;
|
color: #303133;
|
||||||
}
|
}
|
||||||
|
/* Bug #500: 分类加载中的小圆点动画 */
|
||||||
|
.loading-dot {
|
||||||
|
display: inline-block;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #409eff;
|
||||||
|
margin-left: 6px;
|
||||||
|
animation: dotPulse 1s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
@keyframes dotPulse {
|
||||||
|
0%, 100% { opacity: 0.3; transform: scale(0.8); }
|
||||||
|
50% { opacity: 1; transform: scale(1.2); }
|
||||||
|
}
|
||||||
.item-row {
|
.item-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -1448,10 +1481,24 @@ defineExpose({ getList });
|
|||||||
height: auto;
|
height: auto;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
/* Bug #500: 折叠内容添加平滑过渡动画,避免切换时高度跳变 */
|
||||||
:deep(.el-collapse-item__content) {
|
:deep(.el-collapse-item__content) {
|
||||||
padding-bottom: 4px;
|
padding-bottom: 4px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
/* Bug #500: 折叠面板动画容器,添加 overflow:hidden 防止展开时内容溢出导致闪烁 */
|
||||||
:deep(.el-collapse-item__wrap) {
|
:deep(.el-collapse-item__wrap) {
|
||||||
border: none;
|
border: none;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
:deep(.el-collapse-item) {
|
||||||
|
transition: margin 0.2s ease;
|
||||||
|
}
|
||||||
|
/* Bug #500: 分类加载中提示样式 */
|
||||||
|
.category-loading-hint {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -246,7 +246,8 @@
|
|||||||
<el-input-number :min="0" v-model="scope.row.doseQuantity" controls-position="right"
|
<el-input-number :min="0" v-model="scope.row.doseQuantity" controls-position="right"
|
||||||
:controls="false" style="width: 70px; margin-right: 20px"
|
:controls="false" style="width: 70px; margin-right: 20px"
|
||||||
:ref="(el) => (inputRefs.doseQuantity = el)" @input="convertValues(scope.row, scope.$index)"
|
:ref="(el) => (inputRefs.doseQuantity = el)" @input="convertValues(scope.row, scope.$index)"
|
||||||
@keyup.enter.prevent="handleEnter('doseQuantity', scope.row, scope.$index)" />
|
@keyup.enter.prevent="handleEnter('doseQuantity', scope.row, scope.$index)"
|
||||||
|
@change="calculateTotalAmount(scope.row, scope.$index)"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- 剂量单位 -->
|
<!-- 剂量单位 -->
|
||||||
<el-select v-model="scope.row.minUnitCode" style="width: 70px; margin-right: 20px" placeholder=" ">
|
<el-select v-model="scope.row.minUnitCode" style="width: 70px; margin-right: 20px" placeholder=" ">
|
||||||
@@ -294,7 +295,8 @@
|
|||||||
}
|
}
|
||||||
// inputRefs.rateCode.blur();
|
// inputRefs.rateCode.blur();
|
||||||
}
|
}
|
||||||
" :ref="(el) => (inputRefs.rateCode = el)">
|
" :ref="(el) => (inputRefs.rateCode = el)"
|
||||||
|
@change="calculateTotalAmount(scope.row, scope.$index)">
|
||||||
<el-option v-for="dict in rate_code" @click="() => (scope.row.rateCode_dictText = dict.label)"
|
<el-option v-for="dict in rate_code" @click="() => (scope.row.rateCode_dictText = dict.label)"
|
||||||
:key="dict.value" :label="dict.label" :value="dict.value" />
|
:key="dict.value" :label="dict.label" :value="dict.value" />
|
||||||
</el-select>
|
</el-select>
|
||||||
@@ -313,6 +315,7 @@
|
|||||||
data-prop="dispensePerDuration">
|
data-prop="dispensePerDuration">
|
||||||
<el-input-number v-model="scope.row.dispensePerDuration" style="width: 80px" :min="1"
|
<el-input-number v-model="scope.row.dispensePerDuration" style="width: 80px" :min="1"
|
||||||
controls-position="right" :controls="false" :ref="(el) => (inputRefs.dispensePerDuration = el)"
|
controls-position="right" :controls="false" :ref="(el) => (inputRefs.dispensePerDuration = el)"
|
||||||
|
@change="calculateTotalAmount(scope.row, scope.$index)"
|
||||||
@keyup.enter.prevent="
|
@keyup.enter.prevent="
|
||||||
handleEnter('dispensePerDuration', scope.row, scope.$index)
|
handleEnter('dispensePerDuration', scope.row, scope.$index)
|
||||||
">
|
">
|
||||||
|
|||||||
@@ -56,137 +56,6 @@
|
|||||||
<!-- 手术等级 -->
|
<!-- 手术等级 -->
|
||||||
<el-table-column label="手术等级" align="center" prop="surgeryLevel_dictText" width="100" />
|
<el-table-column label="手术等级" align="center" prop="surgeryLevel_dictText" width="100" />
|
||||||
|
|
||||||
<!-- 手术室确认时间 -->
|
|
||||||
<el-table-column label="手术室确认时间" align="center" prop="confirmTime" width="180">
|
|
||||||
<template #default="scope">
|
|
||||||
{{ scope.row.confirmTime ? parseTime(scope.row.confirmTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
|
|
||||||
<!-- 操作列 -->
|
|
||||||
<el-table-column label="操作" align="center" width="150" fixed="right">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
size="small"
|
|
||||||
@click="handleView(scope.row)"
|
|
||||||
>
|
|
||||||
查看
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
size="small"
|
|
||||||
@click="handleEdit(scope.row)"
|
|
||||||
:disabled="scope.row.status !== '0'"
|
|
||||||
>
|
|
||||||
修改
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, onMounted } from 'vue'
|
|
||||||
import { getSurgeryApplicationList, addSurgeryApplication, updateSurgeryApplication } from '@/api/doctorstation/surgery'
|
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
||||||
|
|
||||||
const loading = ref(true)
|
|
||||||
const surgeryList = ref([])
|
|
||||||
|
|
||||||
// 查询手术申请列表
|
|
||||||
const getList = async () => {
|
|
||||||
try {
|
|
||||||
loading.value = true
|
|
||||||
const response = await getSurgeryApplicationList()
|
|
||||||
surgeryList.value = response.data || []
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('获取手术申请列表失败', error)
|
|
||||||
surgeryList.value = []
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增手术申请
|
|
||||||
const handleAdd = () => {
|
|
||||||
// 跳转到新增页面或打开新增弹窗
|
|
||||||
// 这里需要根据实际路由配置调整
|
|
||||||
openSurgeryDialog()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑手术申请
|
|
||||||
const handleEdit = (row) => {
|
|
||||||
if(row.status !== '0') {
|
|
||||||
ElMessage.warning('该申请已提交,无法修改')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
openSurgeryDialog(row)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查看手术申请详情
|
|
||||||
const handleView = (row) => {
|
|
||||||
openSurgeryDialog(row, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 刷新列表
|
|
||||||
const handleRefresh = () => {
|
|
||||||
getList()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开手术申请对话框
|
|
||||||
const openSurgeryDialog = (row, isView = false) => {
|
|
||||||
// 这里需要引入具体的对话框组件或跳转到详情页
|
|
||||||
// 示例代码,具体实现需要根据项目实际情况调整
|
|
||||||
console.log('打开手术申请对话框', row, isView)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取行样式
|
|
||||||
const getRowClassName = ({ row }) => {
|
|
||||||
if (row.status === '1') {
|
|
||||||
return 'success-row'
|
|
||||||
} else if (row.status === '2') {
|
|
||||||
return 'warning-row'
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getList()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 暴露方法给父组件调用,用于提交后刷新列表
|
|
||||||
defineExpose({
|
|
||||||
refreshList: getList
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.surgery-application-container {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-operation-bar {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-button,
|
|
||||||
.refresh-button {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 表格行状态样式 */
|
|
||||||
::v-deep(.success-row) {
|
|
||||||
background-color: #f0f9ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep(.warning-row) {
|
|
||||||
background-color: #fff7e6;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<el-table-column label="手术室确认时间" align="center" prop="operatingRoomConfirmTime" width="180">
|
<el-table-column label="手术室确认时间" align="center" prop="operatingRoomConfirmTime" width="180">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ scope.row.operatingRoomConfirmTime ? parseTime(scope.row.operatingRoomConfirmTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
{{ scope.row.operatingRoomConfirmTime ? parseTime(scope.row.operatingRoomConfirmTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
||||||
|
|||||||
@@ -121,3 +121,25 @@ export function getTestResultPage(queryParams) {
|
|||||||
data: queryParams,
|
data: queryParams,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除申请单(仅待签发状态可删除)
|
||||||
|
*/
|
||||||
|
export function deleteRequestForm(data) {
|
||||||
|
return request({
|
||||||
|
url: '/reg-doctorstation/request-form/delete',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 撤回申请单(已签发状态撤回至待签发)
|
||||||
|
*/
|
||||||
|
export function withdrawRequestForm(data) {
|
||||||
|
return request({
|
||||||
|
url: '/reg-doctorstation/request-form/withdraw',
|
||||||
|
method: 'post',
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -175,10 +175,9 @@ const hasMatchedFields = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/** 查询科室 */
|
/** 查询科室 */
|
||||||
const getLocationInfo = () => {
|
const getLocationInfo = async () => {
|
||||||
getOrgList().then((res) => {
|
const res = await getOrgList();
|
||||||
orgOptions.value = res.data.records;
|
orgOptions.value = res.data.records;
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const recursionFun = (targetDepartment) => {
|
const recursionFun = (targetDepartment) => {
|
||||||
@@ -199,7 +198,12 @@ const recursionFun = (targetDepartment) => {
|
|||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleViewDetail = (row) => {
|
const handleViewDetail = async (row) => {
|
||||||
|
// 确保科室数据已加载,以便将 ID 解析为名称
|
||||||
|
if (!orgOptions.value || orgOptions.value.length === 0) {
|
||||||
|
await getLocationInfo();
|
||||||
|
}
|
||||||
|
|
||||||
currentDetail.value = row;
|
currentDetail.value = row;
|
||||||
// 解析 descJson
|
// 解析 descJson
|
||||||
if (row.descJson) {
|
if (row.descJson) {
|
||||||
@@ -220,10 +224,9 @@ const handleViewDetail = (row) => {
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => patientInfo.value?.encounterId,
|
() => patientInfo.value?.encounterId,
|
||||||
(val) => {
|
async (val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
fetchData();
|
await Promise.all([fetchData(), getLocationInfo()]);
|
||||||
getLocationInfo();
|
|
||||||
} else {
|
} else {
|
||||||
tableData.value = [];
|
tableData.value = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,15 +49,6 @@
|
|||||||
<el-option label="已作废" value="7" />
|
<el-option label="已作废" value="7" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="关键字">
|
|
||||||
<el-input
|
|
||||||
v-model="filterForm.keyword"
|
|
||||||
placeholder="申请单号/检查项目"
|
|
||||||
clearable
|
|
||||||
style="width: 200px"
|
|
||||||
@keyup.enter="handleSearch"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="handleSearch" :loading="loading">
|
<el-button type="primary" @click="handleSearch" :loading="loading">
|
||||||
<el-icon><Search /></el-icon>
|
<el-icon><Search /></el-icon>
|
||||||
@@ -189,22 +180,9 @@ const descJsonData = ref(null);
|
|||||||
const orgOptions = ref([]);
|
const orgOptions = ref([]);
|
||||||
|
|
||||||
// 筛选表单数据
|
// 筛选表单数据
|
||||||
const getDefaultDateRange = () => {
|
|
||||||
const now = new Date();
|
|
||||||
const weekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
||||||
const formatDate = (d) => {
|
|
||||||
const year = d.getFullYear();
|
|
||||||
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
||||||
const day = String(d.getDate()).padStart(2, '0');
|
|
||||||
return `${year}-${month}-${day}`;
|
|
||||||
};
|
|
||||||
return [formatDate(weekAgo), formatDate(now)];
|
|
||||||
};
|
|
||||||
|
|
||||||
const filterForm = ref({
|
const filterForm = ref({
|
||||||
dateRange: getDefaultDateRange(), // 默认近一周
|
dateRange: [], // [startDate, endDate]
|
||||||
status: '', // 申请单状态
|
status: '', // 申请单状态
|
||||||
keyword: '', // 关键字搜索
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
@@ -229,11 +207,6 @@ const fetchData = async () => {
|
|||||||
params.status = filterForm.value.status;
|
params.status = filterForm.value.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加关键字搜索
|
|
||||||
if (filterForm.value.keyword && filterForm.value.keyword.trim()) {
|
|
||||||
params.keyword = filterForm.value.keyword.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await getCheck(params);
|
const res = await getCheck(params);
|
||||||
if (res.code === 200 && res.data) {
|
if (res.code === 200 && res.data) {
|
||||||
const raw = res.data?.records || res.data;
|
const raw = res.data?.records || res.data;
|
||||||
@@ -270,9 +243,8 @@ const handleSearch = async () => {
|
|||||||
* 重置按钮处理
|
* 重置按钮处理
|
||||||
*/
|
*/
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
filterForm.value.dateRange = getDefaultDateRange();
|
filterForm.value.dateRange = [];
|
||||||
filterForm.value.status = '';
|
filterForm.value.status = '';
|
||||||
filterForm.value.keyword = '';
|
|
||||||
fetchData();
|
fetchData();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -372,9 +344,8 @@ watch(
|
|||||||
getLocationInfo();
|
getLocationInfo();
|
||||||
} else {
|
} else {
|
||||||
tableData.value = [];
|
tableData.value = [];
|
||||||
filterForm.value.dateRange = getDefaultDateRange();
|
filterForm.value.dateRange = [];
|
||||||
filterForm.value.status = '';
|
filterForm.value.status = '';
|
||||||
filterForm.value.keyword = '';
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<!--
|
||||||
|
* 住院医生站 — 汇总发药申请:复用药房「发药汇总单」能力
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div class="summary-drug-application">
|
||||||
|
<MedicationSummary />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import MedicationSummary from '@/views/drug/inpatientMedicationDispensing/components/MedicationSummary.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.summary-drug-application {
|
||||||
|
height: 100%;
|
||||||
|
min-height: 400px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -181,10 +181,9 @@ const hasMatchedFields = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/** 查询科室 */
|
/** 查询科室 */
|
||||||
const getLocationInfo = () => {
|
const getLocationInfo = async () => {
|
||||||
getOrgList().then((res) => {
|
const res = await getOrgList();
|
||||||
orgOptions.value = res.data.records;
|
orgOptions.value = res.data.records;
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const recursionFun = (targetDepartment) => {
|
const recursionFun = (targetDepartment) => {
|
||||||
@@ -205,7 +204,12 @@ const recursionFun = (targetDepartment) => {
|
|||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleViewDetail = (row) => {
|
const handleViewDetail = async (row) => {
|
||||||
|
// 确保科室数据已加载,以便将 ID 解析为名称
|
||||||
|
if (!orgOptions.value || orgOptions.value.length === 0) {
|
||||||
|
await getLocationInfo();
|
||||||
|
}
|
||||||
|
|
||||||
currentDetail.value = row;
|
currentDetail.value = row;
|
||||||
// 解析 descJson
|
// 解析 descJson
|
||||||
if (row.descJson) {
|
if (row.descJson) {
|
||||||
@@ -226,10 +230,9 @@ const handleViewDetail = (row) => {
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => patientInfo.value?.encounterId,
|
() => patientInfo.value?.encounterId,
|
||||||
(val) => {
|
async (val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
fetchData();
|
await Promise.all([fetchData(), getLocationInfo()]);
|
||||||
getLocationInfo();
|
|
||||||
} else {
|
} else {
|
||||||
tableData.value = [];
|
tableData.value = [];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,15 @@
|
|||||||
<el-option label="已作废" value="5" />
|
<el-option label="已作废" value="5" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="关键字">
|
||||||
|
<el-input
|
||||||
|
v-model="filterForm.keyword"
|
||||||
|
placeholder="申请单号/检验项目"
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" @click="handleSearch" :loading="loading">
|
<el-button type="primary" @click="handleSearch" :loading="loading">
|
||||||
<el-icon><Search /></el-icon>
|
<el-icon><Search /></el-icon>
|
||||||
@@ -173,7 +182,7 @@ import {computed, getCurrentInstance, ref, watch} from 'vue';
|
|||||||
import {Refresh, Search} from '@element-plus/icons-vue';
|
import {Refresh, Search} from '@element-plus/icons-vue';
|
||||||
import {patientInfo} from '../../store/patient.js';
|
import {patientInfo} from '../../store/patient.js';
|
||||||
import {getInspection} from './api';
|
import {getInspection} from './api';
|
||||||
import {getOrgList} from '@/views/doctorstation/components/api.js';
|
import {getDepartmentList} from '@/api/public.js';
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
const { proxy } = getCurrentInstance();
|
||||||
|
|
||||||
@@ -188,6 +197,7 @@ const orgOptions = ref([]);
|
|||||||
const filterForm = ref({
|
const filterForm = ref({
|
||||||
dateRange: [], // [startDate, endDate]
|
dateRange: [], // [startDate, endDate]
|
||||||
status: '', // 单据状态
|
status: '', // 单据状态
|
||||||
|
keyword: '', // 关键字搜索
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
@@ -212,6 +222,11 @@ const fetchData = async () => {
|
|||||||
params.status = filterForm.value.status;
|
params.status = filterForm.value.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加关键字搜索
|
||||||
|
if (filterForm.value.keyword && filterForm.value.keyword.trim()) {
|
||||||
|
params.keyword = filterForm.value.keyword.trim();
|
||||||
|
}
|
||||||
|
|
||||||
const res = await getInspection(params);
|
const res = await getInspection(params);
|
||||||
if (res.code === 200 && res.data) {
|
if (res.code === 200 && res.data) {
|
||||||
const raw = res.data?.records || res.data;
|
const raw = res.data?.records || res.data;
|
||||||
@@ -251,6 +266,7 @@ const handleReset = () => {
|
|||||||
// 重置筛选条件为默认值
|
// 重置筛选条件为默认值
|
||||||
filterForm.value.dateRange = [];
|
filterForm.value.dateRange = [];
|
||||||
filterForm.value.status = '';
|
filterForm.value.status = '';
|
||||||
|
filterForm.value.keyword = '';
|
||||||
// 重新加载数据
|
// 重新加载数据
|
||||||
fetchData();
|
fetchData();
|
||||||
};
|
};
|
||||||
@@ -331,10 +347,9 @@ const hasMatchedFields = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/** 查询科室 */
|
/** 查询科室 */
|
||||||
const getLocationInfo = () => {
|
const getLocationInfo = async () => {
|
||||||
getOrgList().then((res) => {
|
const res = await getDepartmentList();
|
||||||
orgOptions.value = res.data.records;
|
orgOptions.value = res.data || [];
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const recursionFun = (targetDepartment) => {
|
const recursionFun = (targetDepartment) => {
|
||||||
@@ -345,17 +360,24 @@ const recursionFun = (targetDepartment) => {
|
|||||||
name = obj.name;
|
name = obj.name;
|
||||||
}
|
}
|
||||||
const subObjArray = obj['children'];
|
const subObjArray = obj['children'];
|
||||||
for (let index = 0; index < subObjArray.length; index++) {
|
if (subObjArray && subObjArray.length > 0) {
|
||||||
const item = subObjArray[index];
|
for (let i = 0; i < subObjArray.length; i++) {
|
||||||
|
const item = subObjArray[i];
|
||||||
if (item.id == targetDepartment) {
|
if (item.id == targetDepartment) {
|
||||||
name = item.name;
|
name = item.name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleViewDetail = (row) => {
|
const handleViewDetail = async (row) => {
|
||||||
|
// 确保科室数据已加载,以便将 ID 解析为名称
|
||||||
|
if (!orgOptions.value || orgOptions.value.length === 0) {
|
||||||
|
await getLocationInfo();
|
||||||
|
}
|
||||||
|
|
||||||
currentDetail.value = row;
|
currentDetail.value = row;
|
||||||
// 解析 descJson
|
// 解析 descJson
|
||||||
if (row.descJson) {
|
if (row.descJson) {
|
||||||
@@ -376,7 +398,7 @@ const handleViewDetail = (row) => {
|
|||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => patientInfo.value?.encounterId,
|
() => patientInfo.value?.encounterId,
|
||||||
(val) => {
|
async (val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
// 设置默认日期范围为近7天
|
// 设置默认日期范围为近7天
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
@@ -396,13 +418,13 @@ watch(
|
|||||||
formatDate(today)
|
formatDate(today)
|
||||||
];
|
];
|
||||||
|
|
||||||
fetchData();
|
await Promise.all([fetchData(), getLocationInfo()]);
|
||||||
getLocationInfo();
|
|
||||||
} else {
|
} else {
|
||||||
tableData.value = [];
|
tableData.value = [];
|
||||||
// 重置筛选条件
|
// 重置筛选条件
|
||||||
filterForm.value.dateRange = [];
|
filterForm.value.dateRange = [];
|
||||||
filterForm.value.status = '';
|
filterForm.value.status = '';
|
||||||
|
filterForm.value.keyword = '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
|
|||||||
@@ -176,6 +176,8 @@ const projectWithDepartment = (selectProjectIds, type) => {
|
|||||||
});
|
});
|
||||||
arr.push(searchData);
|
arr.push(searchData);
|
||||||
});
|
});
|
||||||
|
// 保存用户手动选择的发往科室(提交时需要保留)
|
||||||
|
const manualDept = type === 2 ? form.targetDepartment : '';
|
||||||
// 清空科室
|
// 清空科室
|
||||||
form.targetDepartment = '';
|
form.targetDepartment = '';
|
||||||
if (arr.length > 0) {
|
if (arr.length > 0) {
|
||||||
@@ -194,12 +196,18 @@ const projectWithDepartment = (selectProjectIds, type) => {
|
|||||||
// 选中项目中的执行科室id与全部科室数据做匹配
|
// 选中项目中的执行科室id与全部科室数据做匹配
|
||||||
const findItem = findTreeItem(orgOptions.value, obj.orgId);
|
const findItem = findTreeItem(orgOptions.value, obj.orgId);
|
||||||
if (!findItem) {
|
if (!findItem) {
|
||||||
|
// type=2(提交)时,若用户已手动选择发往科室,则允许提交
|
||||||
|
if (type === 2 && manualDept) {
|
||||||
|
form.targetDepartment = manualDept;
|
||||||
|
isRelease = true;
|
||||||
|
} else {
|
||||||
isRelease = false;
|
isRelease = false;
|
||||||
ElMessage({
|
ElMessage({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: '未找到项目执行的科室',
|
message: '未找到项目执行的科室',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (type == 1) {
|
if (type == 1) {
|
||||||
if (isRelease) {
|
if (isRelease) {
|
||||||
form.targetDepartment = findItem.id;
|
form.targetDepartment = findItem.id;
|
||||||
@@ -232,7 +240,7 @@ const submit = () => {
|
|||||||
unitCode: item.priceList[0].unitCode /** 请求单位编码 */,
|
unitCode: item.priceList[0].unitCode /** 请求单位编码 */,
|
||||||
unitPrice: item.priceList[0].price /** 单价 */,
|
unitPrice: item.priceList[0].price /** 单价 */,
|
||||||
totalPrice: item.priceList[0].price /** 总价 */,
|
totalPrice: item.priceList[0].price /** 总价 */,
|
||||||
positionId: item.positionId, //执行科室id
|
positionId: item.positionId || form.targetDepartment, //执行科室id,未配置时使用用户手动选择的科室
|
||||||
ybClassEnum: item.ybClassEnum, //类别医保编码
|
ybClassEnum: item.ybClassEnum, //类别医保编码
|
||||||
conditionId: item.conditionId, //诊断ID
|
conditionId: item.conditionId, //诊断ID
|
||||||
encounterDiagnosisId: item.encounterDiagnosisId, //就诊诊断id
|
encounterDiagnosisId: item.encounterDiagnosisId, //就诊诊断id
|
||||||
|
|||||||
@@ -42,6 +42,40 @@
|
|||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="紧急程度" prop="urgencyLevel" style="width: 100%">
|
||||||
|
<el-select v-model="form.urgencyLevel" placeholder="请选择紧急程度" clearable style="width: 100%">
|
||||||
|
<el-option label="普通" value="routine" />
|
||||||
|
<el-option label="急诊" value="emergency" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="过敏史" prop="allergyHistory" style="width: 100%">
|
||||||
|
<el-input v-model="form.allergyHistory" autocomplete="off" type="textarea" placeholder="如:造影剂过敏史等" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="检查目的" prop="examinationPurpose" style="width: 100%">
|
||||||
|
<el-input v-model="form.examinationPurpose" autocomplete="off" type="textarea" placeholder="请说明检查目的" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="病史摘要" prop="medicalHistorySummary" style="width: 100%">
|
||||||
|
<el-input v-model="form.medicalHistorySummary" autocomplete="off" type="textarea" placeholder="请简要描述患者病史" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="期望检查时间" prop="expectedExaminationTime" style="width: 100%">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="form.expectedExaminationTime"
|
||||||
|
type="datetime"
|
||||||
|
placeholder="请选择期望检查时间"
|
||||||
|
style="width: 100%"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="症状" prop="symptom" style="width: 100%">
|
<el-form-item label="症状" prop="symptom" style="width: 100%">
|
||||||
<el-input v-model="form.symptom" autocomplete="off" type="textarea" />
|
<el-input v-model="form.symptom" autocomplete="off" type="textarea" />
|
||||||
@@ -145,6 +179,11 @@ const transferValue = ref([]);
|
|||||||
const form = reactive({
|
const form = reactive({
|
||||||
// categoryType: '', // 项目类别
|
// categoryType: '', // 项目类别
|
||||||
targetDepartment: '', // 发往科室
|
targetDepartment: '', // 发往科室
|
||||||
|
urgencyLevel: '', // 紧急程度
|
||||||
|
allergyHistory: '', // 过敏史
|
||||||
|
examinationPurpose: '', // 检查目的
|
||||||
|
medicalHistorySummary: '', // 病史摘要
|
||||||
|
expectedExaminationTime: '', // 期望检查时间
|
||||||
symptom: '', // 症状
|
symptom: '', // 症状
|
||||||
sign: '', // 体征
|
sign: '', // 体征
|
||||||
clinicalDiagnosis: '', // 临床诊断
|
clinicalDiagnosis: '', // 临床诊断
|
||||||
|
|||||||
@@ -122,7 +122,6 @@
|
|||||||
|
|
||||||
<el-table-column label="类型" align="center" prop="" width="120">
|
<el-table-column label="类型" align="center" prop="" width="120">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ console.log(scope.row, 1111) }}
|
|
||||||
<el-radio-group
|
<el-radio-group
|
||||||
v-model="scope.row.therapyEnum"
|
v-model="scope.row.therapyEnum"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -270,7 +269,6 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="频次/用法" align="center" prop="" width="180">
|
<el-table-column label="频次/用法" align="center" prop="" width="180">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ console.log(scope.row) }}
|
|
||||||
<span v-if="!scope.row.isEdit && scope.row.adviceType == 1" style="text-align: right">
|
<span v-if="!scope.row.isEdit && scope.row.adviceType == 1" style="text-align: right">
|
||||||
{{
|
{{
|
||||||
[
|
[
|
||||||
@@ -804,8 +802,10 @@ function clickRowDb(row, column, event) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
row.showPopover = false;
|
row.showPopover = false;
|
||||||
// “待签发(已保存 requestId存在)”不允许再编辑;仅“待保存(无requestId)”允许编辑
|
// 允许所有 statusEnum==1 的医嘱进入编辑:
|
||||||
if (row.statusEnum == 1 && !row.requestId) {
|
// 1. 新医嘱(无 requestId):待保存
|
||||||
|
// 2. 护士退回医嘱(有 requestId):退回后状态重置为 DRAFT(1),需允许医生编辑修改后重新签发
|
||||||
|
if (row.statusEnum == 1) {
|
||||||
// 确保治疗类型为字符串,方便与单选框 label 对齐,默认为长期医嘱('1')
|
// 确保治疗类型为字符串,方便与单选框 label 对齐,默认为长期医嘱('1')
|
||||||
row.therapyEnum = String(row.therapyEnum ?? '1');
|
row.therapyEnum = String(row.therapyEnum ?? '1');
|
||||||
row.isEdit = true;
|
row.isEdit = true;
|
||||||
@@ -879,14 +879,13 @@ function handleDiagnosisChange(item) {
|
|||||||
function handleFocus(row, index) {
|
function handleFocus(row, index) {
|
||||||
rowIndex.value = index;
|
rowIndex.value = index;
|
||||||
row.showPopover = true;
|
row.showPopover = true;
|
||||||
// el-popover 懒渲染,需要等两帧组件才会挂载
|
|
||||||
const adviceType = row.adviceType !== undefined ? row.adviceType : adviceQueryParams.value.adviceType;
|
const adviceType = row.adviceType !== undefined ? row.adviceType : adviceQueryParams.value.adviceType;
|
||||||
// 用 adviceType + categoryCode 组合查找匹配的选项
|
// 用 adviceType + categoryCode 组合查找匹配的选项
|
||||||
const selectValue = (adviceType == 1 && row.categoryCode) ? '1-' + row.categoryCode : adviceType;
|
const selectValue = (adviceType == 1 && row.categoryCode) ? '1-' + row.categoryCode : adviceType;
|
||||||
const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType);
|
const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType);
|
||||||
// 诊疗(3)和手术(6)没有categoryCode,传空字符串给refresh,由子组件转为undefined不发送
|
// 修复Bug #486:当行没有显式选择医嘱类型时(row.adviceType为undefined),
|
||||||
// 药品(1)才有categoryCode(如'1'=中成药,'2'=西药,'4'=中草药)
|
// 不传categoryCode,让搜索在全药库中进行;只有行已选择类型时才用对应categoryCode过滤
|
||||||
const categoryCode = selectedItem ? selectedItem.categoryCode : (adviceQueryParams.value.categoryCode || '');
|
const categoryCode = row.adviceType !== undefined ? (selectedItem ? selectedItem.categoryCode : '') : '';
|
||||||
const searchKey = row.adviceName || '';
|
const searchKey = row.adviceName || '';
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
@@ -923,8 +922,9 @@ function handleChange(value) {
|
|||||||
// 用 adviceType + categoryCode 组合查找匹配的选项
|
// 用 adviceType + categoryCode 组合查找匹配的选项
|
||||||
const selectValue = (adviceType == 1 && row?.categoryCode) ? '1-' + row.categoryCode : adviceType;
|
const selectValue = (adviceType == 1 && row?.categoryCode) ? '1-' + row.categoryCode : adviceType;
|
||||||
const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType);
|
const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType);
|
||||||
// 诊疗/手术的categoryCode为空字符串,由子组件转为undefined不发送
|
// 修复Bug #486:当行没有显式选择医嘱类型时(row?.adviceType为undefined),
|
||||||
const categoryCode = selectedItem ? selectedItem.categoryCode : (adviceQueryParams.value.categoryCode || '');
|
// 不传categoryCode,让搜索在全药库中进行;只有行已选择类型时才用对应categoryCode过滤
|
||||||
|
const categoryCode = row?.adviceType !== undefined ? (selectedItem ? selectedItem.categoryCode : '') : '';
|
||||||
tableRef.refresh(adviceType, categoryCode, value);
|
tableRef.refresh(adviceType, categoryCode, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1214,12 +1214,8 @@ function handleSave() {
|
|||||||
getListInfo(false);
|
getListInfo(false);
|
||||||
bindMethod.value = {};
|
bindMethod.value = {};
|
||||||
nextId.value == 1;
|
nextId.value == 1;
|
||||||
|
|
||||||
// 处方保存成功后,自动将医嘱信息同步至患者处置记录
|
|
||||||
// handleEmrTreatment();
|
|
||||||
} else {
|
} else {
|
||||||
proxy.$modal.msgError(res.message);
|
proxy.$modal.msgError(res.message);
|
||||||
conso;
|
|
||||||
isSaving.value = false;
|
isSaving.value = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1319,6 +1315,9 @@ function handleCancelEdit(row, index) {
|
|||||||
function handleSaveSign(row, index) {
|
function handleSaveSign(row, index) {
|
||||||
if (row.adviceType != 2) {
|
if (row.adviceType != 2) {
|
||||||
let itemNo = row.adviceType == 1 ? row.methodCode : row.adviceDefinitionId;
|
let itemNo = row.adviceType == 1 ? row.methodCode : row.adviceDefinitionId;
|
||||||
|
if (!itemNo) {
|
||||||
|
console.warn('绑定设备检查跳过:itemNo为空(adviceType=' + row.adviceType + ', adviceName=' + row.adviceName + ')');
|
||||||
|
} else {
|
||||||
getBindDevice({ typeCode: row.adviceType, itemNo: itemNo }).then((res) => {
|
getBindDevice({ typeCode: row.adviceType, itemNo: itemNo }).then((res) => {
|
||||||
if (res.data.length == 0) {
|
if (res.data.length == 0) {
|
||||||
return;
|
return;
|
||||||
@@ -1336,6 +1335,7 @@ function handleSaveSign(row, index) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 更新UI状态
|
// 更新UI状态
|
||||||
row.isEdit = false;
|
row.isEdit = false;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</div>
|
</div>
|
||||||
<el-button type="primary">划价组套</el-button>
|
<el-button type="primary" @click="openGroupSetDialog">划价组套</el-button>
|
||||||
</div>
|
</div>
|
||||||
<!-- 弹窗内容 - 左右布局 -->
|
<!-- 弹窗内容 - 左右布局 -->
|
||||||
<div style="display: flex; gap: 20px; height: 70vh">
|
<div style="display: flex; gap: 20px; height: 70vh">
|
||||||
@@ -147,6 +147,7 @@
|
|||||||
<el-select
|
<el-select
|
||||||
v-model="scope.row.selectUnitCode"
|
v-model="scope.row.selectUnitCode"
|
||||||
placeholder="单位"
|
placeholder="单位"
|
||||||
|
filterable
|
||||||
style="width: 100px"
|
style="width: 100px"
|
||||||
@change="unitCodeChange(scope.row)"
|
@change="unitCodeChange(scope.row)"
|
||||||
>
|
>
|
||||||
@@ -171,30 +172,32 @@
|
|||||||
v-if="scope.row.adviceType == 3"
|
v-if="scope.row.adviceType == 3"
|
||||||
clearable
|
clearable
|
||||||
filterable
|
filterable
|
||||||
|
:filter-method="(val) => filterOptions(val, scope.row, 'departmentOptions')"
|
||||||
v-model="scope.row.positionId"
|
v-model="scope.row.positionId"
|
||||||
placeholder="选择科室"
|
placeholder="选择科室"
|
||||||
style="width: 220px"
|
style="width: 220px"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="dept in departmentOptions"
|
v-for="dept in getFilteredOptions(scope.row, 'departmentOptions')"
|
||||||
:key="dept.id"
|
:key="dept.id"
|
||||||
:label="dept.name"
|
:label="dept.name"
|
||||||
:value="dept.id"
|
:value="String(dept.id)"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
<el-select
|
<el-select
|
||||||
v-if="scope.row.adviceType == 2"
|
v-if="scope.row.adviceType == 2"
|
||||||
clearable
|
clearable
|
||||||
filterable
|
filterable
|
||||||
|
:filter-method="(val) => filterOptions(val, scope.row, 'locationOptions')"
|
||||||
v-model="scope.row.positionId"
|
v-model="scope.row.positionId"
|
||||||
placeholder="选择药房/耗材房"
|
placeholder="选择药房/耗材房"
|
||||||
style="width: 220px"
|
style="width: 220px"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="dept in locationOptions"
|
v-for="dept in getFilteredOptions(scope.row, 'locationOptions')"
|
||||||
:key="dept.value"
|
:key="dept.value"
|
||||||
:label="dept.label"
|
:label="dept.label"
|
||||||
:value="dept.value"
|
:value="String(dept.value)"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
</template>
|
||||||
@@ -247,6 +250,57 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
<!-- 划价组套选择对话框 -->
|
||||||
|
<el-dialog v-model="groupSetDialogVisible" title="划价组套选择" width="600px" :close-on-click-modal="false" append-to-body>
|
||||||
|
<div style="margin-bottom: 15px; display: flex; align-items: center; gap: 10px">
|
||||||
|
<el-input
|
||||||
|
v-model="groupSetSearchText"
|
||||||
|
placeholder="请输入组套名称搜索"
|
||||||
|
clearable
|
||||||
|
style="width: 250px"
|
||||||
|
@keyup.enter="loadGroupSets"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<el-button icon="Search" @click="loadGroupSets" />
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
<el-radio-group v-model="groupSetRange" @change="loadGroupSets">
|
||||||
|
<el-radio-button :label="1">个人</el-radio-button>
|
||||||
|
<el-radio-button :label="2">科室</el-radio-button>
|
||||||
|
<el-radio-button :label="3">全院</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
:data="groupSetList"
|
||||||
|
v-loading="groupSetLoading"
|
||||||
|
highlight-current-row
|
||||||
|
border
|
||||||
|
stripe
|
||||||
|
@current-change="handleGroupSetSelect"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||||
|
<el-table-column label="组套名称" min-width="180" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.name || scope.row.Name || '' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="使用范围" width="100" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ getRangeText(scope.row) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="明细数量" width="100" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.detailList?.length || 0 }} 项
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div style="margin-top: 15px; text-align: right">
|
||||||
|
<el-button @click="groupSetDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="applyGroupSet" :disabled="!selectedGroupSet">应用</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@@ -254,6 +308,7 @@ import {computed, getCurrentInstance, onMounted, reactive, ref, watch} from 'vue
|
|||||||
import {ElMessage} from 'element-plus';
|
import {ElMessage} from 'element-plus';
|
||||||
import {formatDateStr} from '@/utils/index';
|
import {formatDateStr} from '@/utils/index';
|
||||||
import {getAdviceBaseInfo, getDiseaseTreatmentInitLoc, getOrgList} from './api.js';
|
import {getAdviceBaseInfo, getDiseaseTreatmentInitLoc, getOrgList} from './api.js';
|
||||||
|
import {getOrderGroup} from '@/views/doctorstation/components/api.js';
|
||||||
import useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
const { proxy } = getCurrentInstance();
|
||||||
@@ -312,6 +367,8 @@ const locationOptions = ref([]);
|
|||||||
const searchText = ref('');
|
const searchText = ref('');
|
||||||
const userId = ref('');
|
const userId = ref('');
|
||||||
const orgId = ref('');
|
const orgId = ref('');
|
||||||
|
// 下拉框模糊搜索关键字(按行存储)
|
||||||
|
const filterKeywords = ref({});
|
||||||
const queryParams = ref({
|
const queryParams = ref({
|
||||||
pageSize: 100,
|
pageSize: 100,
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
@@ -389,6 +446,14 @@ const submitData = reactive({
|
|||||||
});
|
});
|
||||||
const adviceLoading = ref(false);
|
const adviceLoading = ref(false);
|
||||||
|
|
||||||
|
// 划价组套相关
|
||||||
|
const groupSetDialogVisible = ref(false);
|
||||||
|
const groupSetList = ref([]);
|
||||||
|
const groupSetLoading = ref(false);
|
||||||
|
const groupSetSearchText = ref('');
|
||||||
|
const groupSetRange = ref(2);
|
||||||
|
const selectedGroupSet = ref(null);
|
||||||
|
|
||||||
// 【优化核心】计算总金额 - 使用计算属性实现实时更新
|
// 【优化核心】计算总金额 - 使用计算属性实现实时更新
|
||||||
const totalAmount = computed(() => {
|
const totalAmount = computed(() => {
|
||||||
return feeItemsList.value.reduce((sum, item) => {
|
return feeItemsList.value.reduce((sum, item) => {
|
||||||
@@ -424,10 +489,15 @@ watch(
|
|||||||
|
|
||||||
// 加载科室选项
|
// 加载科室选项
|
||||||
function loadDepartmentOptions() {
|
function loadDepartmentOptions() {
|
||||||
getOrgList().then((res) => {
|
getOrgList()
|
||||||
|
.then((res) => {
|
||||||
if (res.data && res.data.records && res.data.records.length > 0) {
|
if (res.data && res.data.records && res.data.records.length > 0) {
|
||||||
departmentOptions.value = res.data.records[0].children || [];
|
departmentOptions.value = res.data.records[0].children || [];
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.warn('科室列表加载失败(可能无权限)');
|
||||||
|
departmentOptions.value = [];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,9 +517,33 @@ function getAdviceBaseInfos() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
function getDiseaseInitLoc() {
|
function getDiseaseInitLoc() {
|
||||||
getDiseaseTreatmentInitLoc(16).then((response) => {
|
getDiseaseTreatmentInitLoc(16)
|
||||||
|
.then((response) => {
|
||||||
console.log('Disease Treatment Init Loc:', response);
|
console.log('Disease Treatment Init Loc:', response);
|
||||||
locationOptions.value = response.data.locationOptions;
|
locationOptions.value = response.data.locationOptions;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.warn('位置列表加载失败(可能无权限)');
|
||||||
|
locationOptions.value = [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 下拉框模糊搜索过滤
|
||||||
|
function filterOptions(val, row, optionsKey) {
|
||||||
|
const key = row.adviceDefinitionId + '_' + optionsKey;
|
||||||
|
filterKeywords.value[key] = val;
|
||||||
|
}
|
||||||
|
function getFilteredOptions(row, optionsKey) {
|
||||||
|
const key = row.adviceDefinitionId + '_' + optionsKey;
|
||||||
|
const keyword = filterKeywords.value[key];
|
||||||
|
const options = optionsKey === 'departmentOptions' ? departmentOptions.value : locationOptions.value;
|
||||||
|
if (!keyword || keyword.trim() === '') {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
const lower = keyword.toLowerCase();
|
||||||
|
return options.filter(item => {
|
||||||
|
const name = (item.name || item.label || '').toLowerCase();
|
||||||
|
const id = String(item.id || item.value || '').toLowerCase();
|
||||||
|
return name.includes(lower) || id.includes(lower);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// 获取组套类型文本
|
// 获取组套类型文本
|
||||||
@@ -527,14 +621,32 @@ function selectChange(row) {
|
|||||||
const price = row.priceList?.[0]?.price || 0;
|
const price = row.priceList?.[0]?.price || 0;
|
||||||
//获取大小单位
|
//获取大小单位
|
||||||
const uniqueUnitCodes = getUnitCodeOptions(row);
|
const uniqueUnitCodes = getUnitCodeOptions(row);
|
||||||
|
// 设置默认单位:优先使用 minUnitCode(小单位),回退到 unitCode(大单位)
|
||||||
|
let defaultUnitCode = '';
|
||||||
|
if (row.minUnitCode) {
|
||||||
|
defaultUnitCode = String(row.minUnitCode);
|
||||||
|
} else if (row.unitCode) {
|
||||||
|
defaultUnitCode = String(row.unitCode);
|
||||||
|
}
|
||||||
|
// 设置默认执行科室/位置(统一转为字符串,避免 el-select 类型不匹配)
|
||||||
|
let defaultPositionId = undefined;
|
||||||
|
if (row.adviceType === 3 && departmentOptions.value.length > 0) {
|
||||||
|
// 诊疗:优先使用患者所在科室,否则取第一个科室
|
||||||
|
const patientOrgId = props.patientInfo.organizationId;
|
||||||
|
const matched = departmentOptions.value.find(d => String(d.id) === String(patientOrgId));
|
||||||
|
defaultPositionId = matched ? String(matched.id) : String(departmentOptions.value[0].id);
|
||||||
|
} else if (row.adviceType === 2 && locationOptions.value.length > 0) {
|
||||||
|
// 耗材:默认取第一个药房/耗材房
|
||||||
|
defaultPositionId = String(locationOptions.value[0].value);
|
||||||
|
}
|
||||||
//插入费用列表
|
//插入费用列表
|
||||||
feeItemsList.value.push({
|
feeItemsList.value.push({
|
||||||
...row,
|
...row,
|
||||||
uniqueUnitCodes: uniqueUnitCodes,
|
uniqueUnitCodes: uniqueUnitCodes,
|
||||||
unitPrice: (price / (row.partPercent || 1)).toFixed(6), // 根据拆零比计算单价
|
unitPrice: (price / (row.partPercent || 1)).toFixed(6), // 根据拆零比计算单价
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
// positionId: row.positionId === null || row.positionId === undefined ? orgId : row.positionId, // 默认执行科室
|
positionId: defaultPositionId, // 默认执行科室/位置(字符串类型)
|
||||||
selectUnitCode: row.minUnitCode, // 默认选择小单位
|
selectUnitCode: defaultUnitCode, // 默认选择小单位
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,6 +760,101 @@ function resetData() {
|
|||||||
searchText.value = '';
|
searchText.value = '';
|
||||||
executeTime.value = '';
|
executeTime.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 划价组套相关功能
|
||||||
|
function openGroupSetDialog() {
|
||||||
|
groupSetDialogVisible.value = true;
|
||||||
|
groupSetSearchText.value = '';
|
||||||
|
selectedGroupSet.value = null;
|
||||||
|
loadGroupSets();
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadGroupSets() {
|
||||||
|
groupSetLoading.value = true;
|
||||||
|
getOrderGroup({ organizationId: orgId.value })
|
||||||
|
.then((res) => {
|
||||||
|
const data = res?.data || {};
|
||||||
|
if (groupSetRange.value === 1) {
|
||||||
|
groupSetList.value = data.personalList || [];
|
||||||
|
} else if (groupSetRange.value === 2) {
|
||||||
|
groupSetList.value = data.organizationList || [];
|
||||||
|
} else {
|
||||||
|
groupSetList.value = data.hospitalList || [];
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
console.warn('组套列表加载失败(可能无权限)');
|
||||||
|
groupSetList.value = [];
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
groupSetLoading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRangeText(row) {
|
||||||
|
// 后端未返回rangeCode,根据当前选中的范围标签显示
|
||||||
|
const rangeMap = { 1: '个人', 2: '科室', 3: '全院' };
|
||||||
|
if (row.rangeCode_dictText) return row.rangeCode_dictText;
|
||||||
|
return rangeMap[groupSetRange.value] || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleGroupSetSelect(row) {
|
||||||
|
selectedGroupSet.value = row;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyGroupSet() {
|
||||||
|
if (!selectedGroupSet.value) {
|
||||||
|
ElMessage.warning('请先选择一个组套');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const detailList = selectedGroupSet.value.detailList;
|
||||||
|
if (!detailList || detailList.length === 0) {
|
||||||
|
ElMessage.warning('该组套没有明细项');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let successCount = 0;
|
||||||
|
detailList.forEach((item) => {
|
||||||
|
const orderDetail = item.orderDetailInfos || {};
|
||||||
|
const feeItem = {
|
||||||
|
adviceDefinitionId: item.orderDefinitionId || orderDetail.adviceDefinitionId,
|
||||||
|
adviceName: orderDetail.adviceName || item.orderDefinitionName || '未知项目',
|
||||||
|
adviceType: orderDetail.adviceType,
|
||||||
|
unitPrice: orderDetail.unitPrice,
|
||||||
|
minUnitPrice: orderDetail.minUnitPrice,
|
||||||
|
inventoryList: orderDetail.inventoryList || [],
|
||||||
|
priceList: orderDetail.priceList || [],
|
||||||
|
partPercent: orderDetail.partPercent || 1,
|
||||||
|
positionId: item.positionId || orderDetail.positionId,
|
||||||
|
defaultLotNumber: orderDetail.defaultLotNumber,
|
||||||
|
unitCode: item.unitCode || orderDetail.unitCode,
|
||||||
|
unitCode_dictText: item.unitCodeName || orderDetail.unitCode_dictText,
|
||||||
|
minUnitCode: orderDetail.minUnitCode,
|
||||||
|
doseUnitCode: orderDetail.doseUnitCode,
|
||||||
|
categoryCode: orderDetail.categoryCode,
|
||||||
|
pharmacologyCategoryCode: orderDetail.pharmacologyCategoryCode,
|
||||||
|
partAttributeEnum: orderDetail.partAttributeEnum,
|
||||||
|
chargeItemDefinitionId: orderDetail.chargeItemDefinitionId,
|
||||||
|
adviceTableName: orderDetail.adviceTableName,
|
||||||
|
methodCode: item.methodCode || orderDetail.methodCode,
|
||||||
|
rateCode: item.rateCode || orderDetail.rateCode,
|
||||||
|
dose: item.dose || orderDetail.dose,
|
||||||
|
doseQuantity: item.doseQuantity,
|
||||||
|
dispensePerDuration: item.dispensePerDuration || orderDetail.dispensePerDuration,
|
||||||
|
dosageInstruction: orderDetail.dosageInstruction,
|
||||||
|
skinTestFlag: orderDetail.skinTestFlag,
|
||||||
|
injectFlag: orderDetail.injectFlag,
|
||||||
|
quantity: item.quantity || 1,
|
||||||
|
chrgitmLv_dictText: orderDetail.chrgitmLv_dictText,
|
||||||
|
volume: orderDetail.volume,
|
||||||
|
};
|
||||||
|
selectChange(feeItem);
|
||||||
|
successCount++;
|
||||||
|
});
|
||||||
|
if (successCount > 0) {
|
||||||
|
ElMessage.success(`已添加 ${successCount} 项组套费用`);
|
||||||
|
}
|
||||||
|
groupSetDialogVisible.value = false;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -273,8 +273,9 @@ function handleSearch() {
|
|||||||
// 清除缓存(搜索时需要重新加载)
|
// 清除缓存(搜索时需要重新加载)
|
||||||
patientDataCache.value.clear();
|
patientDataCache.value.clear();
|
||||||
|
|
||||||
// 重新加载所有已展开病区的患者列表
|
// 通过递增 key 强制重新渲染树组件,触发重新加载所有病区患者列表
|
||||||
reloadAllPatients();
|
// 此时 searchKey 已有值,loadPatientList 会将 searchKey 传给后端进行过滤
|
||||||
|
treeKey.value += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 暴露方法供外部调用
|
// 暴露方法供外部调用
|
||||||
|
|||||||
@@ -174,7 +174,6 @@ const activeNames = ref([]);
|
|||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const prescriptionList = ref([]);
|
const prescriptionList = ref([]);
|
||||||
const deadline = ref(formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59');
|
const deadline = ref(formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59');
|
||||||
const therapyEnum = ref(undefined);
|
|
||||||
const { proxy } = getCurrentInstance();
|
const { proxy } = getCurrentInstance();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const chooseAll = ref(false);
|
const chooseAll = ref(false);
|
||||||
@@ -190,6 +189,10 @@ const props = defineProps({
|
|||||||
deadline: {
|
deadline: {
|
||||||
type: String,
|
type: String,
|
||||||
},
|
},
|
||||||
|
therapyEnum: {
|
||||||
|
type: Number,
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleGetPrescription() {
|
function handleGetPrescription() {
|
||||||
@@ -200,7 +203,7 @@ function handleGetPrescription() {
|
|||||||
encounterIds: encounterIds,
|
encounterIds: encounterIds,
|
||||||
pageSize: 10000,
|
pageSize: 10000,
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
therapyEnum: therapyEnum.value,
|
therapyEnum: props.therapyEnum,
|
||||||
exeStatus: props.exeStatus,
|
exeStatus: props.exeStatus,
|
||||||
requestStatus: props.requestStatus,
|
requestStatus: props.requestStatus,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
:clearable="false"
|
:clearable="false"
|
||||||
@change="handleGetPrescription"
|
@change="handleGetPrescription"
|
||||||
/>
|
/>
|
||||||
<el-radio-group v-model="therapyEnum" class="ml20" @change="handleRadioChange">
|
<el-radio-group v-model="therapyEnum" class="ml20" @change="handleTherapyChange">
|
||||||
<el-radio :value="undefined">全部</el-radio>
|
<el-radio :value="undefined">全部</el-radio>
|
||||||
<el-radio :value="1">长期</el-radio>
|
<el-radio :value="1">长期</el-radio>
|
||||||
<el-radio :value="2">临时</el-radio>
|
<el-radio :value="2">临时</el-radio>
|
||||||
@@ -79,6 +79,7 @@
|
|||||||
:exeStatus="exeStatus"
|
:exeStatus="exeStatus"
|
||||||
:requestStatus="requestStatus"
|
:requestStatus="requestStatus"
|
||||||
:deadline="deadline"
|
:deadline="deadline"
|
||||||
|
:therapyEnum="therapyEnum"
|
||||||
/>
|
/>
|
||||||
<SummaryMedicineList v-else />
|
<SummaryMedicineList v-else />
|
||||||
<!-- <el-tabs v-model="activeName" class="demo-tabs centered-tabs" @tab-change="handleClick">
|
<!-- <el-tabs v-model="activeName" class="demo-tabs centered-tabs" @tab-change="handleClick">
|
||||||
@@ -120,6 +121,7 @@ const requestStatus = ref(RequestStatus.COMPLETED);
|
|||||||
const chooseAll = ref(false);
|
const chooseAll = ref(false);
|
||||||
const drugType = ref('1');
|
const drugType = ref('1');
|
||||||
const isDetails = ref('1');
|
const isDetails = ref('1');
|
||||||
|
const therapyEnum = ref(undefined);
|
||||||
|
|
||||||
// 存储子组件引用的对象
|
// 存储子组件引用的对象
|
||||||
const prescriptionRefs = ref();
|
const prescriptionRefs = ref();
|
||||||
@@ -175,6 +177,10 @@ function handleRadioChange(value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleTherapyChange() {
|
||||||
|
handleGetPrescription();
|
||||||
|
}
|
||||||
|
|
||||||
function handleExecute() {
|
function handleExecute() {
|
||||||
proxy.$refs['prescriptionRefs'].handleMedicineSummary();
|
proxy.$refs['prescriptionRefs'].handleMedicineSummary();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -229,7 +229,7 @@ import {adviceCancel, adviceExecute, adviceNoExecute, getPrescriptionList} from
|
|||||||
import {patientInfoList} from '../../components/store/patient.js';
|
import {patientInfoList} from '../../components/store/patient.js';
|
||||||
import {lotNumberMatch} from '@/api/public';
|
import {lotNumberMatch} from '@/api/public';
|
||||||
import {formatDateStr} from '@/utils/index';
|
import {formatDateStr} from '@/utils/index';
|
||||||
import {getCurrentInstance, nextTick, ref} from 'vue';
|
import {getCurrentInstance, nextTick, ref, provide} from 'vue';
|
||||||
|
|
||||||
const activeNames = ref([]);
|
const activeNames = ref([]);
|
||||||
const prescriptionList = ref([]);
|
const prescriptionList = ref([]);
|
||||||
@@ -239,6 +239,8 @@ const therapyEnum = ref(undefined);
|
|||||||
const { proxy } = getCurrentInstance();
|
const { proxy } = getCurrentInstance();
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const chooseAll = ref(false);
|
const chooseAll = ref(false);
|
||||||
|
// 独立维护选中行ID集合,避免el-table内部selection状态异常导致联动全选
|
||||||
|
const selectedRowIds = ref(new Set());
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
exeStatus: {
|
exeStatus: {
|
||||||
type: Number,
|
type: Number,
|
||||||
@@ -280,9 +282,10 @@ function handleGetPrescription() {
|
|||||||
exeStatus: props.exeStatus,
|
exeStatus: props.exeStatus,
|
||||||
requestStatus: props.requestStatus,
|
requestStatus: props.requestStatus,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
// try {
|
try {
|
||||||
|
const records = res?.data?.records || [];
|
||||||
// 根据encounterId分组
|
// 根据encounterId分组
|
||||||
const groupedPrescriptions = res.data.records.reduce((groups, prescription) => {
|
const groupedPrescriptions = records.reduce((groups, prescription) => {
|
||||||
// 获取当前医嘱全部执行频次
|
// 获取当前医嘱全部执行频次
|
||||||
let rate;
|
let rate;
|
||||||
let times;
|
let times;
|
||||||
@@ -427,18 +430,26 @@ function handleGetPrescription() {
|
|||||||
|
|
||||||
// 将分组结果转换为数组形式
|
// 将分组结果转换为数组形式
|
||||||
prescriptionList.value = Object.values(groupedPrescriptions);
|
prescriptionList.value = Object.values(groupedPrescriptions);
|
||||||
loading.value = false;
|
|
||||||
// 默认选中全部行
|
// 默认选中全部行
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
defaultSelectAllRows();
|
defaultSelectAllRows();
|
||||||
});
|
});
|
||||||
// } catch {
|
} catch (error) {
|
||||||
// loading.value = false;
|
console.error('医嘱执行-获取处方列表数据处理失败:', error);
|
||||||
// }
|
prescriptionList.value = [];
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error('医嘱执行-获取处方列表接口失败:', error);
|
||||||
|
prescriptionList.value = [];
|
||||||
|
loading.value = false;
|
||||||
});
|
});
|
||||||
chooseAll.value = false;
|
chooseAll.value = false;
|
||||||
} else {
|
} else {
|
||||||
prescriptionList.value = [];
|
prescriptionList.value = [];
|
||||||
|
selectedRowIds.value.clear();
|
||||||
|
loading.value = false;
|
||||||
// proxy.$message.warning('请选择患者');
|
// proxy.$message.warning('请选择患者');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -458,11 +469,19 @@ function handleExecute() {
|
|||||||
console.log(list, 'list');
|
console.log(list, 'list');
|
||||||
adviceExecute({ exeDate: exeDate.value, adviceExecuteDetailList: list }).then((res) => {
|
adviceExecute({ exeDate: exeDate.value, adviceExecuteDetailList: list }).then((res) => {
|
||||||
if (res.code == 200) {
|
if (res.code == 200) {
|
||||||
proxy.$modal.msgSuccess(res.msg || '医嘱执行成功');
|
|
||||||
handleGetPrescription();
|
handleGetPrescription();
|
||||||
|
// 仅当选中医嘱中包含药品/耗材类医嘱时,才调用耗材批号匹配
|
||||||
|
const hasMedOrDevice = list.some(
|
||||||
|
(item) =>
|
||||||
|
item.adviceTable === 'med_medication_request' ||
|
||||||
|
String(item.adviceTable || '').includes('device'),
|
||||||
|
);
|
||||||
|
if (hasMedOrDevice) {
|
||||||
lotNumberMatch({ encounterIdList: encounterIds }, { skipErrorMsg: true }).catch((error) => {
|
lotNumberMatch({ encounterIdList: encounterIds }, { skipErrorMsg: true }).catch((error) => {
|
||||||
console.warn('lotNumberMatch failed after adviceExecute:', error);
|
console.warn('lotNumberMatch failed after adviceExecute:', error);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
proxy.$modal.msgSuccess(res.msg || '医嘱执行成功');
|
||||||
} else {
|
} else {
|
||||||
proxy.$modal.msgError(res.msg || '医嘱执行失败');
|
proxy.$modal.msgError(res.msg || '医嘱执行失败');
|
||||||
}
|
}
|
||||||
@@ -497,15 +516,30 @@ function handleCancel() {
|
|||||||
let list = getSelectRows();
|
let list = getSelectRows();
|
||||||
let producerIds = [];
|
let producerIds = [];
|
||||||
list.forEach((item) => {
|
list.forEach((item) => {
|
||||||
|
// 从 exePerformRecordList 直接提取 procedureId,过滤空值避免后端SQL异常
|
||||||
|
const procedureIds = (item.exePerformRecordList || [])
|
||||||
|
.map((record) => record.procedureId)
|
||||||
|
.filter((id) => id != null && id !== '');
|
||||||
|
if (procedureIds.length === 0 && (!item.procedureIds || item.procedureIds.length === 0)) {
|
||||||
|
proxy.$modal.msgError('请选择已执行的医嘱记录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const ids = procedureIds.length > 0 ? procedureIds : item.procedureIds;
|
||||||
producerIds.push(
|
producerIds.push(
|
||||||
...item.procedureIds.map((value) => {
|
...ids
|
||||||
|
.filter((value) => value != null && value !== '')
|
||||||
|
.map((value) => {
|
||||||
return {
|
return {
|
||||||
procedureId: value,
|
procedureId: value,
|
||||||
therapyEnum: item.therapyEnum,
|
therapyEnum: Number(item.therapyEnum),
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
if (producerIds.length === 0) {
|
||||||
|
proxy.$modal.msgError('未找到有效的执行记录,无法取消执行');
|
||||||
|
return;
|
||||||
|
}
|
||||||
adviceCancel({ adviceExecuteDetailList: producerIds }).then((res) => {
|
adviceCancel({ adviceExecuteDetailList: producerIds }).then((res) => {
|
||||||
if (res.code == 200) {
|
if (res.code == 200) {
|
||||||
proxy.$modal.msgSuccess(res.msg || '取消执行成功');
|
proxy.$modal.msgSuccess(res.msg || '取消执行成功');
|
||||||
@@ -513,14 +547,20 @@ function handleCancel() {
|
|||||||
} else {
|
} else {
|
||||||
proxy.$modal.msgError(res.msg || '取消执行失败');
|
proxy.$modal.msgError(res.msg || '取消执行失败');
|
||||||
}
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
proxy.$modal.msgError('取消执行失败,请稍后重试');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSelectRows() {
|
function getSelectRows() {
|
||||||
// 获取选中的医嘱信息
|
// 优先从独立维护的selectedRowIds集合中获取选中行,避免el-table内部selection状态异常
|
||||||
let list = [];
|
const list = [];
|
||||||
prescriptionList.value.forEach((item, index) => {
|
prescriptionList.value.forEach((item) => {
|
||||||
list = [...list, ...proxy.$refs['tableRef' + index][0].getSelectionRows()];
|
item.forEach((row) => {
|
||||||
|
if (selectedRowIds.value.has(row.requestId)) {
|
||||||
|
list.push(row);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@@ -596,12 +636,14 @@ function handelSwicthChange(value) {
|
|||||||
if (value) {
|
if (value) {
|
||||||
// 全选:选中所有行并联动checkbox
|
// 全选:选中所有行并联动checkbox
|
||||||
item.forEach((row) => {
|
item.forEach((row) => {
|
||||||
|
selectedRowIds.value.add(row.requestId);
|
||||||
tableRef[0].toggleRowSelection(row, true);
|
tableRef[0].toggleRowSelection(row, true);
|
||||||
selectAllCheckboxesInRow(row);
|
selectAllCheckboxesInRow(row);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 取消全选:取消选中所有行并联动checkbox
|
// 取消全选:取消选中所有行并联动checkbox
|
||||||
item.forEach((row) => {
|
item.forEach((row) => {
|
||||||
|
selectedRowIds.value.delete(row.requestId);
|
||||||
tableRef[0].toggleRowSelection(row, false);
|
tableRef[0].toggleRowSelection(row, false);
|
||||||
unselectAllCheckboxesInRow(row);
|
unselectAllCheckboxesInRow(row);
|
||||||
});
|
});
|
||||||
@@ -612,11 +654,14 @@ function handelSwicthChange(value) {
|
|||||||
|
|
||||||
// 默认选中全部行
|
// 默认选中全部行
|
||||||
function defaultSelectAllRows() {
|
function defaultSelectAllRows() {
|
||||||
|
// 清空并重建选中集合
|
||||||
|
selectedRowIds.value.clear();
|
||||||
prescriptionList.value.forEach((item, index) => {
|
prescriptionList.value.forEach((item, index) => {
|
||||||
const tableRef = proxy.$refs['tableRef' + index];
|
const tableRef = proxy.$refs['tableRef' + index];
|
||||||
if (tableRef && tableRef[0]) {
|
if (tableRef && tableRef[0]) {
|
||||||
// 选中该表格的所有行
|
// 选中该表格的所有行
|
||||||
item.forEach((row) => {
|
item.forEach((row) => {
|
||||||
|
selectedRowIds.value.add(row.requestId);
|
||||||
tableRef[0].toggleRowSelection(row, true);
|
tableRef[0].toggleRowSelection(row, true);
|
||||||
// 同时选中该行内部的所有checkbox
|
// 同时选中该行内部的所有checkbox
|
||||||
selectAllCheckboxesInRow(row);
|
selectAllCheckboxesInRow(row);
|
||||||
@@ -696,13 +741,14 @@ function checkAndToggleRowSelection(row) {
|
|||||||
const tableRef = proxy.$refs['tableRef' + tableIndex];
|
const tableRef = proxy.$refs['tableRef' + tableIndex];
|
||||||
if (tableRef && tableRef[0]) {
|
if (tableRef && tableRef[0]) {
|
||||||
const isAllSelected = isAllCheckboxesSelected(row);
|
const isAllSelected = isAllCheckboxesSelected(row);
|
||||||
const selectedRows = tableRef[0].getSelectionRows();
|
const isCurrentlySelected = selectedRowIds.value.has(row.requestId);
|
||||||
const isCurrentlySelected = selectedRows.some((r) => r.requestId === row.requestId);
|
|
||||||
|
|
||||||
// 根据checkbox状态更新表格行选中状态
|
// 根据checkbox状态更新表格行选中状态
|
||||||
if (isAllSelected && !isCurrentlySelected) {
|
if (isAllSelected && !isCurrentlySelected) {
|
||||||
|
selectedRowIds.value.add(row.requestId);
|
||||||
tableRef[0].toggleRowSelection(row, true);
|
tableRef[0].toggleRowSelection(row, true);
|
||||||
} else if (!isAllSelected && isCurrentlySelected) {
|
} else if (!isAllSelected && isCurrentlySelected) {
|
||||||
|
selectedRowIds.value.delete(row.requestId);
|
||||||
tableRef[0].toggleRowSelection(row, false);
|
tableRef[0].toggleRowSelection(row, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -715,9 +761,11 @@ function handleRowSelect(selection, row, tableIndex) {
|
|||||||
const isSelected = selection.some((item) => item.requestId === row.requestId);
|
const isSelected = selection.some((item) => item.requestId === row.requestId);
|
||||||
|
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
|
selectedRowIds.value.add(row.requestId);
|
||||||
// 选中行时,选中该行内部的所有checkbox
|
// 选中行时,选中该行内部的所有checkbox
|
||||||
selectAllCheckboxesInRow(row);
|
selectAllCheckboxesInRow(row);
|
||||||
} else {
|
} else {
|
||||||
|
selectedRowIds.value.delete(row.requestId);
|
||||||
// 取消选中行时,取消选中该行内部的所有checkbox
|
// 取消选中行时,取消选中该行内部的所有checkbox
|
||||||
unselectAllCheckboxesInRow(row);
|
unselectAllCheckboxesInRow(row);
|
||||||
}
|
}
|
||||||
@@ -734,11 +782,13 @@ function handleSelectAll(selection, tableIndex) {
|
|||||||
if (selection.length > 0) {
|
if (selection.length > 0) {
|
||||||
// 全选时,选中所有行内部的所有checkbox
|
// 全选时,选中所有行内部的所有checkbox
|
||||||
tableData.forEach((row) => {
|
tableData.forEach((row) => {
|
||||||
|
selectedRowIds.value.add(row.requestId);
|
||||||
selectAllCheckboxesInRow(row);
|
selectAllCheckboxesInRow(row);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 取消全选时,取消选中所有行内部的所有checkbox
|
// 取消全选时,取消选中所有行内部的所有checkbox
|
||||||
tableData.forEach((row) => {
|
tableData.forEach((row) => {
|
||||||
|
selectedRowIds.value.delete(row.requestId);
|
||||||
unselectAllCheckboxesInRow(row);
|
unselectAllCheckboxesInRow(row);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -750,17 +800,13 @@ function handleSelectAll(selection, tableIndex) {
|
|||||||
// 更新全选开关状态
|
// 更新全选开关状态
|
||||||
function updateChooseAllStatus() {
|
function updateChooseAllStatus() {
|
||||||
let allSelected = true;
|
let allSelected = true;
|
||||||
prescriptionList.value.forEach((item, index) => {
|
prescriptionList.value.forEach((item) => {
|
||||||
const tableRef = proxy.$refs['tableRef' + index];
|
item.forEach((row) => {
|
||||||
if (tableRef && tableRef[0]) {
|
if (!selectedRowIds.value.has(row.requestId)) {
|
||||||
const selectedRows = tableRef[0].getSelectionRows();
|
|
||||||
if (selectedRows.length !== item.length) {
|
|
||||||
allSelected = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
allSelected = false;
|
allSelected = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
});
|
||||||
chooseAll.value = allSelected;
|
chooseAll.value = allSelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {getCurrentInstance} from 'vue';
|
import {getCurrentInstance, ref, nextTick, provide} from 'vue';
|
||||||
import PatientList from '../components/patientList.vue';
|
import PatientList from '../components/patientList.vue';
|
||||||
import PrescriptionList from './components/prescriptionList.vue';
|
import PrescriptionList from './components/prescriptionList.vue';
|
||||||
import { RequestStatus } from '@/utils/medicalConstants';
|
import { RequestStatus } from '@/utils/medicalConstants';
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ function handleGetPrescription() {
|
|||||||
getPrescriptionList({
|
getPrescriptionList({
|
||||||
encounterIds: encounterIds,
|
encounterIds: encounterIds,
|
||||||
requestStatus: props.requestStatus,
|
requestStatus: props.requestStatus,
|
||||||
therapyEnum: type.value === 1 ? undefined : type.value === 2 ? 1 : 2, // 1=全部(不传), 2=长期(1), 3=临时(2)
|
...(type.value !== undefined ? { therapyEnum: type.value } : {}),
|
||||||
pageSize: 10000,
|
pageSize: 10000,
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
@@ -288,6 +288,12 @@ function handleCheck() {
|
|||||||
function handleCancel() {
|
function handleCancel() {
|
||||||
let list = getSelectRows();
|
let list = getSelectRows();
|
||||||
if (list.length > 0) {
|
if (list.length > 0) {
|
||||||
|
// 校验已发药的医嘱不允许退回
|
||||||
|
let dispensedItems = list.filter(item => item.dispenseStatus === 4);
|
||||||
|
if (dispensedItems.length > 0) {
|
||||||
|
proxy.$message.error('该医嘱已发药,无法退回');
|
||||||
|
return;
|
||||||
|
}
|
||||||
cancel(list).then((res) => {
|
cancel(list).then((res) => {
|
||||||
if (res.code == 200) {
|
if (res.code == 200) {
|
||||||
proxy.$modal.msgSuccess(res.msg);
|
proxy.$modal.msgSuccess(res.msg);
|
||||||
|
|||||||
@@ -112,6 +112,19 @@
|
|||||||
<el-form ref="dynamicForm" :model="formData" label-width="100px" :rules="formRules">
|
<el-form ref="dynamicForm" :model="formData" label-width="100px" :rules="formRules">
|
||||||
<div class="page-bottom">
|
<div class="page-bottom">
|
||||||
<el-row :gutter="24">
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item style="margin-top: 15px" label="录入日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.recordingDate"
|
||||||
|
type="date"
|
||||||
|
placeholder="请选择日期"
|
||||||
|
size="small"
|
||||||
|
format="YYYY/MM/DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item style="margin-top: 15px" label="录入时间">
|
<el-form-item style="margin-top: 15px" label="录入时间">
|
||||||
<div class="input-time-inline">
|
<div class="input-time-inline">
|
||||||
@@ -766,6 +779,7 @@ const receptionTime = ref(null);
|
|||||||
|
|
||||||
// 表单数据 - 体征录入
|
// 表单数据 - 体征录入
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
|
recordingDate: '',
|
||||||
timePoint: '',
|
timePoint: '',
|
||||||
temperature: '',
|
temperature: '',
|
||||||
systolicPressure: '',
|
systolicPressure: '',
|
||||||
@@ -872,6 +886,7 @@ function getPatientDetial() {
|
|||||||
// 默认查询今天的数据
|
// 默认查询今天的数据
|
||||||
const today = moment().format('YYYY-MM-DD');
|
const today = moment().format('YYYY-MM-DD');
|
||||||
receptionTime.value = [today, today];
|
receptionTime.value = [today, today];
|
||||||
|
formData.value.recordingDate = today;
|
||||||
// 自动加载数据
|
// 自动加载数据
|
||||||
getPatientList();
|
getPatientList();
|
||||||
listPatient(queryParams.value).then((res) => {
|
listPatient(queryParams.value).then((res) => {
|
||||||
@@ -920,6 +935,7 @@ function handleRowClick(row) {
|
|||||||
formData.value = {
|
formData.value = {
|
||||||
...formData.value,
|
...formData.value,
|
||||||
id: row.id,
|
id: row.id,
|
||||||
|
recordingDate: row.recordingDate || '',
|
||||||
timePoint: convertTimePoint(row.timePoint) || '',
|
timePoint: convertTimePoint(row.timePoint) || '',
|
||||||
temperature: row.temperature || '',
|
temperature: row.temperature || '',
|
||||||
systolicPressure: row.systolicPressure || '',
|
systolicPressure: row.systolicPressure || '',
|
||||||
@@ -1038,7 +1054,7 @@ function confirmCharge() {
|
|||||||
|
|
||||||
params.vitalSignsCode = vitalSignsCode;
|
params.vitalSignsCode = vitalSignsCode;
|
||||||
params.vitalSignsValues = vitalSignsValues;
|
params.vitalSignsValues = vitalSignsValues;
|
||||||
params.recordingDate = moment(new Date()).format('YYYY-MM-DD');
|
params.recordingDate = formData.value.recordingDate || moment(new Date()).format('YYYY-MM-DD');
|
||||||
|
|
||||||
addVitalSigns(params).then(res => {
|
addVitalSigns(params).then(res => {
|
||||||
console.log('保存成功:', res);
|
console.log('保存成功:', res);
|
||||||
@@ -1047,6 +1063,7 @@ function confirmCharge() {
|
|||||||
getPatientList();
|
getPatientList();
|
||||||
// 清空表单
|
// 清空表单
|
||||||
formData.value = {
|
formData.value = {
|
||||||
|
recordingDate: '',
|
||||||
timePoint: '',
|
timePoint: '',
|
||||||
temperature: '',
|
temperature: '',
|
||||||
systolicPressure: '',
|
systolicPressure: '',
|
||||||
|
|||||||
@@ -198,7 +198,38 @@
|
|||||||
↓
|
↓
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="queue-actions-right">
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部控制面板 -->
|
||||||
|
<div class="footer-section">
|
||||||
|
<!-- Bug #411:诊室快速过滤栏,筛选维度从科室改为诊室 -->
|
||||||
|
<div class="filter-section">
|
||||||
|
<div class="filter-left">
|
||||||
|
<div class="filter-label">
|
||||||
|
③ 诊室快速过滤栏
|
||||||
|
</div>
|
||||||
|
<div class="filter-button-wrapper">
|
||||||
|
<el-button
|
||||||
|
:type="selectedRoom === 'all' ? 'primary' : ''"
|
||||||
|
size="small"
|
||||||
|
@click="selectedRoom = 'all'"
|
||||||
|
>
|
||||||
|
全部
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-for="room in uniqueRooms"
|
||||||
|
:key="room"
|
||||||
|
:type="selectedRoom === room ? 'primary' : ''"
|
||||||
|
size="small"
|
||||||
|
@click="selectedRoom = room"
|
||||||
|
>
|
||||||
|
{{ room }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="filter-right">
|
||||||
<el-button
|
<el-button
|
||||||
:type="showOnlyWaiting ? 'primary' : ''"
|
:type="showOnlyWaiting ? 'primary' : ''"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -215,38 +246,6 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 底部控制面板 -->
|
|
||||||
<div class="footer-section">
|
|
||||||
<!-- 就诊科室快速过滤栏 -->
|
|
||||||
<div class="filter-section">
|
|
||||||
<div class="filter-label">
|
|
||||||
③ 就诊科室快速过滤栏
|
|
||||||
</div>
|
|
||||||
<div class="filter-select-wrapper">
|
|
||||||
<el-select
|
|
||||||
v-model="selectedDept"
|
|
||||||
placeholder="请选择就诊科室"
|
|
||||||
clearable
|
|
||||||
filterable
|
|
||||||
style="width: 100%"
|
|
||||||
size="default"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
label="全部"
|
|
||||||
value="all"
|
|
||||||
/>
|
|
||||||
<el-option
|
|
||||||
v-for="dept in departmentList"
|
|
||||||
:key="dept.id"
|
|
||||||
:label="dept.name"
|
|
||||||
:value="dept.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 叫号控制板 -->
|
<!-- 叫号控制板 -->
|
||||||
<div class="call-control-section">
|
<div class="call-control-section">
|
||||||
@@ -648,7 +647,6 @@ import { Refresh } from '@element-plus/icons-vue'
|
|||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import {
|
import {
|
||||||
getCandidatePool,
|
getCandidatePool,
|
||||||
getLocationTree,
|
|
||||||
getTriageQueueList,
|
getTriageQueueList,
|
||||||
addToQueue,
|
addToQueue,
|
||||||
removeFromQueue,
|
removeFromQueue,
|
||||||
@@ -681,10 +679,8 @@ const selectedCandidates = ref([])
|
|||||||
// 显示选项
|
// 显示选项
|
||||||
const showOnlyWaiting = ref(false)
|
const showOnlyWaiting = ref(false)
|
||||||
|
|
||||||
// 科室过滤(改为使用就诊科室)
|
// Bug #411:诊室过滤,替代原来的科室下拉框(selectedDept/departmentList 已移除)
|
||||||
const selectedDept = ref('all')
|
const selectedRoom = ref('all')
|
||||||
// 就诊科室列表
|
|
||||||
const departmentList = ref([])
|
|
||||||
|
|
||||||
// 修复【#397】:动态获取当前科室名称
|
// 修复【#397】:动态获取当前科室名称
|
||||||
const currentDeptName = computed(() => {
|
const currentDeptName = computed(() => {
|
||||||
@@ -907,12 +903,11 @@ const mapFrontendStatusToBackend = (status) => {
|
|||||||
// 从数据库加载队列
|
// 从数据库加载队列
|
||||||
const loadQueueFromDb = async () => {
|
const loadQueueFromDb = async () => {
|
||||||
try {
|
try {
|
||||||
// 如果选择了具体科室,就按科室加载;否则加载当前登录人科室(后端默认)
|
// Bug #411:不再按科室选筛加载,后端默认按当前登录人科室查询
|
||||||
const organizationId = selectedDept.value !== 'all' ? selectedDept.value : undefined
|
const organizationId = undefined
|
||||||
// 只查询今天的患者
|
// 只查询今天的患者
|
||||||
const today = new Date()
|
const today = new Date()
|
||||||
const todayStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`
|
const todayStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`
|
||||||
console.log('【心内科】loadQueueFromDb 开始:organizationId=', organizationId, 'date=', todayStr, 'selectedDept=', selectedDept.value)
|
|
||||||
const res = await getTriageQueueList({ organizationId, date: todayStr }).catch((err) => {
|
const res = await getTriageQueueList({ organizationId, date: todayStr }).catch((err) => {
|
||||||
console.error('【心内科】loadQueueFromDb 请求异常:', err)
|
console.error('【心内科】loadQueueFromDb 请求异常:', err)
|
||||||
return { code: 500, msg: err?.message || '请求失败', data: null }
|
return { code: 500, msg: err?.message || '请求失败', data: null }
|
||||||
@@ -1158,55 +1153,29 @@ const loadDataFromApi = async () => {
|
|||||||
// 原始数据存储(用于过滤)
|
// 原始数据存储(用于过滤)
|
||||||
const originalCandidatePoolList = ref(getInitialCandidatePoolList())
|
const originalCandidatePoolList = ref(getInitialCandidatePoolList())
|
||||||
|
|
||||||
// 辅助函数:扁平化科室树形结构
|
// 过滤后的智能候选池数据(按诊室过滤)
|
||||||
const flattenDepartmentTree = (tree, result = []) => {
|
|
||||||
if (!Array.isArray(tree)) return result
|
|
||||||
tree.forEach(node => {
|
|
||||||
if (node.id && node.name) {
|
|
||||||
result.push({ id: node.id, name: node.name })
|
|
||||||
}
|
|
||||||
if (node.children && Array.isArray(node.children)) {
|
|
||||||
flattenDepartmentTree(node.children, result)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载就诊科室列表
|
|
||||||
const loadDepartmentList = async () => {
|
|
||||||
try {
|
|
||||||
const response = await getLocationTree()
|
|
||||||
if (response && response.data) {
|
|
||||||
// 扁平化树形结构
|
|
||||||
departmentList.value = flattenDepartmentTree(response.data)
|
|
||||||
console.log('【心内科】已加载就诊科室列表:', departmentList.value.length, '个科室')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('【心内科】加载就诊科室列表失败:', error)
|
|
||||||
ElMessage.warning('加载就诊科室列表失败,使用默认数据')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取选中科室的名称
|
|
||||||
const getSelectedDeptName = () => {
|
|
||||||
if (selectedDept.value === 'all') return null
|
|
||||||
const dept = departmentList.value.find(d => d.id === selectedDept.value)
|
|
||||||
return dept ? dept.name : null
|
|
||||||
}
|
|
||||||
|
|
||||||
// 过滤后的智能候选池数据
|
|
||||||
const filteredCandidatePoolList = computed(() => {
|
const filteredCandidatePoolList = computed(() => {
|
||||||
if (selectedDept.value === 'all') {
|
if (selectedRoom.value === 'all') {
|
||||||
return originalCandidatePoolList.value
|
return originalCandidatePoolList.value
|
||||||
}
|
}
|
||||||
const deptName = getSelectedDeptName()
|
return originalCandidatePoolList.value.filter(item => item.room === selectedRoom.value)
|
||||||
if (!deptName) return originalCandidatePoolList.value
|
|
||||||
return originalCandidatePoolList.value.filter(item => item.room === deptName)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 原始队列数据存储(用于过滤)
|
// 原始队列数据存储(用于过滤)
|
||||||
const originalQueueList = ref(getInitialQueueList())
|
const originalQueueList = ref(getInitialQueueList())
|
||||||
|
|
||||||
|
// 动态计算已加载数据中的唯一诊室列表(依赖上方两个 ref,确保声明顺序正确)
|
||||||
|
const uniqueRooms = computed(() => {
|
||||||
|
const rooms = new Set()
|
||||||
|
originalCandidatePoolList.value.forEach(item => {
|
||||||
|
if (item.room && item.room !== '-') rooms.add(item.room)
|
||||||
|
})
|
||||||
|
originalQueueList.value.forEach(item => {
|
||||||
|
if (item.room && item.room !== '-') rooms.add(item.room)
|
||||||
|
})
|
||||||
|
return Array.from(rooms).sort()
|
||||||
|
})
|
||||||
|
|
||||||
const parseMmSsToSeconds = (mmss) => {
|
const parseMmSsToSeconds = (mmss) => {
|
||||||
if (!mmss || typeof mmss !== 'string') return 0
|
if (!mmss || typeof mmss !== 'string') return 0
|
||||||
const [mm, ss] = mmss.split(':')
|
const [mm, ss] = mmss.split(':')
|
||||||
@@ -1223,19 +1192,16 @@ const formatSecondsToMmSs = (totalSeconds) => {
|
|||||||
return `${mm}:${ss}`
|
return `${mm}:${ss}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 过滤后的智能队列数据(同时考虑科室过滤和状态过滤)
|
// 过滤后的智能队列数据(Bug #411:诊室过滤 + 状态过滤)
|
||||||
const filteredQueueList = computed(() => {
|
const filteredQueueList = computed(() => {
|
||||||
let filtered = originalQueueList.value
|
let filtered = originalQueueList.value
|
||||||
|
|
||||||
// 先过滤掉"已完成"状态的患者(无论什么情况都不显示)
|
// 先过滤掉"已完成"状态的患者(无论什么情况都不显示)
|
||||||
filtered = filtered.filter(item => item.status !== '已完成')
|
filtered = filtered.filter(item => item.status !== '已完成')
|
||||||
|
|
||||||
// 再按科室过滤
|
// 再按诊室过滤
|
||||||
if (selectedDept.value !== 'all') {
|
if (selectedRoom.value !== 'all') {
|
||||||
const deptName = getSelectedDeptName()
|
filtered = filtered.filter(item => item.room === selectedRoom.value)
|
||||||
if (deptName) {
|
|
||||||
filtered = filtered.filter(item => item.room === deptName)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 再按状态过滤(只显示等待)
|
// 再按状态过滤(只显示等待)
|
||||||
@@ -1746,16 +1712,12 @@ const handleNextPatient = async () => {
|
|||||||
reqData.id = selectedQueueRow.value.id
|
reqData.id = selectedQueueRow.value.id
|
||||||
reqData.organizationId = selectedQueueRow.value.organizationId
|
reqData.organizationId = selectedQueueRow.value.organizationId
|
||||||
} else {
|
} else {
|
||||||
// 如果没有选中患者,使用查询条件(兼容旧逻辑)
|
// Bug #411:已移除 selectedDept,改为从队列数据中动态获取科室
|
||||||
let orgId = selectedDept.value !== 'all' ? selectedDept.value : undefined
|
|
||||||
// "全科"模式:优先用"当前叫号中/第一个等待"所在科室
|
|
||||||
if (orgId == null) {
|
|
||||||
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
||||||
const waiting = originalQueueList.value.find((i) => i.status === '等待')
|
const waiting = originalQueueList.value.find((i) => i.status === '等待')
|
||||||
console.log('【心内科】handleNextPatient 查找:叫号中=', calling?.patientName, '等待=', waiting?.patientName)
|
console.log('【心内科】handleNextPatient 查找:叫号中=', calling?.patientName, '等待=', waiting?.patientName)
|
||||||
orgId = calling?.organizationId ?? waiting?.organizationId
|
const orgId = calling?.organizationId ?? waiting?.organizationId
|
||||||
console.log('【心内科】handleNextPatient 确定的 orgId=', orgId)
|
console.log('【心内科】handleNextPatient 确定的 orgId=', orgId)
|
||||||
}
|
|
||||||
if (orgId != null) {
|
if (orgId != null) {
|
||||||
reqData.organizationId = orgId
|
reqData.organizationId = orgId
|
||||||
}
|
}
|
||||||
@@ -1784,13 +1746,9 @@ const handleSkip = async () => {
|
|||||||
reqData.id = selectedQueueRow.value.id
|
reqData.id = selectedQueueRow.value.id
|
||||||
reqData.organizationId = selectedQueueRow.value.organizationId
|
reqData.organizationId = selectedQueueRow.value.organizationId
|
||||||
} else {
|
} else {
|
||||||
// 如果没有选中患者,使用查询条件(兼容旧逻辑)
|
// 如果没有选中患者,使用当前叫号中的科室
|
||||||
let orgId = selectedDept.value !== 'all' ? selectedDept.value : undefined
|
|
||||||
// “全科”模式:优先用“当前叫号中”所在科室
|
|
||||||
if (orgId == null) {
|
|
||||||
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
||||||
orgId = calling?.organizationId
|
const orgId = calling?.organizationId
|
||||||
}
|
|
||||||
if (orgId != null) {
|
if (orgId != null) {
|
||||||
reqData.organizationId = orgId
|
reqData.organizationId = orgId
|
||||||
}
|
}
|
||||||
@@ -1818,13 +1776,9 @@ const handleComplete = async () => {
|
|||||||
reqData.id = selectedQueueRow.value.id
|
reqData.id = selectedQueueRow.value.id
|
||||||
reqData.organizationId = selectedQueueRow.value.organizationId
|
reqData.organizationId = selectedQueueRow.value.organizationId
|
||||||
} else {
|
} else {
|
||||||
// 如果没有选中患者,使用查询条件(兼容旧逻辑)
|
// 如果没有选中患者,使用当前叫号中的科室
|
||||||
let orgId = selectedDept.value !== 'all' ? selectedDept.value : undefined
|
|
||||||
// “全科”模式:优先用“当前叫号中”所在科室
|
|
||||||
if (orgId == null) {
|
|
||||||
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
||||||
orgId = calling?.organizationId
|
const orgId = calling?.organizationId
|
||||||
}
|
|
||||||
if (orgId != null) {
|
if (orgId != null) {
|
||||||
reqData.organizationId = orgId
|
reqData.organizationId = orgId
|
||||||
}
|
}
|
||||||
@@ -1852,13 +1806,9 @@ const handleRequeue = async () => {
|
|||||||
reqData.id = selectedQueueRow.value.id
|
reqData.id = selectedQueueRow.value.id
|
||||||
reqData.organizationId = selectedQueueRow.value.organizationId
|
reqData.organizationId = selectedQueueRow.value.organizationId
|
||||||
} else {
|
} else {
|
||||||
// 如果没有选中患者,使用查询条件(兼容旧逻辑)
|
// 如果没有选中患者,使用当前叫号中的科室
|
||||||
let orgId = selectedDept.value !== 'all' ? selectedDept.value : undefined
|
|
||||||
// “全科”模式:优先用“当前叫号中”所在科室
|
|
||||||
if (orgId == null) {
|
|
||||||
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
||||||
orgId = calling?.organizationId
|
const orgId = calling?.organizationId
|
||||||
}
|
|
||||||
if (orgId != null) {
|
if (orgId != null) {
|
||||||
reqData.organizationId = orgId
|
reqData.organizationId = orgId
|
||||||
}
|
}
|
||||||
@@ -2121,8 +2071,6 @@ const handleTestRule = () => {
|
|||||||
|
|
||||||
// 组件挂载
|
// 组件挂载
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 加载就诊科室列表
|
|
||||||
loadDepartmentList()
|
|
||||||
// 初始化:优先从后端加载,失败则回退本地假数据
|
// 初始化:优先从后端加载,失败则回退本地假数据
|
||||||
loadDataFromApi()
|
loadDataFromApi()
|
||||||
startWaitingTimer()
|
startWaitingTimer()
|
||||||
@@ -2244,10 +2192,6 @@ onUnmounted(() => {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.queue-actions-right {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.candidate-actions {
|
.candidate-actions {
|
||||||
@@ -2269,6 +2213,12 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
.filter-section {
|
.filter-section {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.filter-left {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
.filter-label {
|
.filter-label {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@@ -2277,8 +2227,18 @@ onUnmounted(() => {
|
|||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-select-wrapper {
|
.filter-button-wrapper {
|
||||||
width: 100%;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-right {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
align-self: flex-end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user