13 Commits

Author SHA1 Message Date
关羽
dafa5961c4 Fix Bug #489: 护士校对界面重复待校对记录 - SQL JOIN 乘法修复
根因: AdviceProcessAppMapper.xml 中 LEFT JOIN med_medication_dispense
直接关联时,一条药品医嘱若有多条发药记录会产生多行结果(SQL 笛卡尔积),
导致护士校对界面显示重复的待校对记录。

修复: 将直接 JOIN 改为 ROW_NUMBER() 子查询,每个 med_req_id 只取最新
一条发药记录的状态,避免行倍增。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 15:30:17 +08:00
赵云
f223192ec5 Fix Bug #502: 【住院护士站-汇总发药申请】顶部医嘱类型(长期/临时)过滤按钮点击无响应
根因:父组件 index.vue 中 therapyEnum 变量未声明为 ref,且未通过 props 传递给子组件 prescriptionList.vue,
导致点击"长期/临时"按钮时数据流断裂,子组件 API 调用始终使用本地未变化的 therapyEnum 值。

修复:
1. index.vue 新增 const therapyEnum = ref(undefined)
2. index.vue 新增 handleTherapyChange() 调用 handleGetPrescription() 刷新列表
3. index.vue 将 therapyEnum 作为 prop 传入 PrescriptionList
4. prescriptionList.vue 将本地 therapyEnum ref 改为 props 接收
2026-05-11 15:30:17 +08:00
赵云
c7956a116b fix: 恢复 Bug #497 的后端修改 + 数据库字段同步 (ALTER TABLE doc_request_form ADD COLUMN status) 2026-05-11 15:30:17 +08:00
关羽
845354e863 fix: 还原 Bug #443/#475/#477/#486/#497 引入的 getRequestForm 编译错误 2026-05-11 15:30:17 +08:00
赵云
56ea4b6af1 fix: 完整回退 Bug #497 的后端修改(SQL/Java接口/Impl/Dto) 2026-05-11 15:30:17 +08:00
赵云
703e9ead43 Fix Bug #480: [住院护士站-医嘱执行] 非耗材类医嘱执行报"耗材库存"错误且全选逻辑联动异常
根因分析:
1. 非耗材类医嘱执行报"耗材库存"错误: handleExecute 中无条件调用 lotNumberMatch,
   后端会校验该就诊下所有待发放耗材库存,即使当前执行的是口服药等非耗材类医嘱
2. 全选联动异常: msgSuccess 在 handleGetPrescription 之前执行,数据刷新后
   defaultSelectAllRows 重新选中所有行,用户关闭弹窗后看到全选效果

修复方案:
1. 增加医嘱类型判断,仅当选中医嘱包含药品(med_medication_request)或耗材(device)
   类型时才调用 lotNumberMatch
2. 调整执行顺序:先刷新数据再显示成功弹窗,避免用户看到数据刷新的副作用

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 15:30:17 +08:00
赵云
a43f98cc5a fix: 完全回退 Bug #497 引入的 drf.status 字段(数据库不存在) 2026-05-11 15:30:17 +08:00
wangjian963
9215c288d3 506 门诊挂号:门诊诊前退号后,数据库多表状态值变更与 PRD 定义不符
CommonConstants.AppointmentOrderStatus 常量 → OrderStatus 枚举重构
   新增枚举:0=患者取消 / 1=有效 / 2=系统取消 / 3=已完成
   退号流程加乐观锁防并发,slot 状态改回待约,退号日志独立事务 修复 XML 中 Integer 比较用字符串的问题
Bug #411 — 诊室过滤栏从科室下拉框改为诊室按钮组
2026-05-11 15:30:17 +08:00
关羽
777ba71c7d Fix Bug #486: [住院医生工作站-临床医嘱] 医嘱检索框不支持全局模糊搜索,未选"医嘱类型"时检索结果为空
根因:handleFocus/handleChange 中 categoryCode 的计算逻辑错误。当新增行未选择
医嘱类型时(row.adviceType 为 undefined),代码回退到 adviceQueryParams 的默认值并
匹配到具体药品分类(如西药 categoryCode='2'),导致搜索被限制在单一分类而非全局药库。

修复:简化 categoryCode 判定为 `row.adviceType !== undefined ? selectedItem.categoryCode : ''`,
未选类型时传空 categoryCode,使 searchKey 在全药库范围内模糊匹配。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 14:07:50 +08:00
关羽
c3dfd3eb21 Fix Bug #499: 【住院医生工作站-检查申请】检查申请列表缺失查询过滤功能,不符合临床高效检索要求
补充修复:
1. 后端 get-inspection 接口新增 keyword 关键字参数(检验申请关键字搜索)
2. 新增 deleteRequestForm 接口(仅待签发状态可删除)
3. 新增 withdrawRequestForm 接口(已签发状态撤回至待签发)
4. RequestForm 实体新增 status 字段(用于撤回方法状态校验)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 14:07:50 +08:00
关羽
8093f8acda Fix Bug #477: 住院医生工作站-住院检查申请详情弹窗中"发往科室"字段显示为短横线(-),未正常获取数据
根因:handleViewDetail 为同步方法,点击详情时 getLocationInfo 尚未返回,
orgOptions 为空导致 recursionFun 无法将 targetDepartment ID 解析为科室名称。

修复:
1. 前端(4个申请组件):handleViewDetail 改为 async,解析 descJson 前确保 orgOptions 已加载
2. 前端:watch encounterId 改为 Promise.all 并行加载数据和科室列表
3. 后端:新增 keyword 关键字筛选参数(申请单号/检查项目模糊匹配)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 14:03:40 +08:00
关羽
8fae6fe3d5 Fix Bug #491: 【执行科室配置】保存配置时系统报错
根因: addOrEditOrgLoc 方法中 organizationService.getById() 返回 null 时
直接调用 .getName() 导致 NullPointerException。当数据库中某条执行科室配置
关联的 organizationId 对应的科室记录已被删除时触发此问题。

修复: 在调用 getName() 前增加 null 检查,返回"未知科室"作为降级提示。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 14:01:15 +08:00
关羽
5af86494dd Fix Bug #500: 【门诊医生站】检查申请右侧"检查项目分类"切换时,界面出现明显抖动/闪烁
根因分析:
1. el-collapse accordion 模式下快速切换分类时,连续的折叠/展开动画重叠,
   Element Plus 在动画过程中重新计算面板高度,导致高度跳变和白屏闪烁
2. 折叠容器缺少 overflow:hidden,动画过渡期间内容溢出造成闪烁

修复方案:
1. 添加 isAnimating 防抖标志,handleCollapseChange 中 300ms 内忽略后续点击
   (与 CSS 过渡时长一致),让当前动画完整执行后再响应下一次切换
2. .collapse-scroll 添加 overflow-x:hidden,防止水平方向溢出
3. :deep(.el-collapse-item__wrap) 添加 overflow:hidden 替代 will-change:height,
   避免强制 GPU 合成层带来的性能开销

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 13:49:33 +08:00
26 changed files with 486 additions and 700 deletions

View File

@@ -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("已预约");
} }

View File

@@ -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 + "时间冲突");
} }

View File

@@ -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.setPayStatus(0); }
updateOrder.setCancelTime(now);
updateOrder.setCancelReason("门诊退号"); // 乐观锁更新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);
// 已存在则不重复插入(防止唯一约束异常) // 已存在则不重复插入(防止唯一约束异常)

View File

@@ -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")

View File

@@ -51,4 +51,20 @@ public interface IRequestFormManageAppService {
* @return 申请单 * @return 申请单
*/ */
IPage<RequestFormPageDto> getRequestFormPage(RequestFormDto requestFormDto); IPage<RequestFormPageDto> getRequestFormPage(RequestFormDto requestFormDto);
/**
* 删除申请单(仅待签发状态可删除)
*
* @param requestFormId 申请单ID
* @return 结果
*/
R<?> deleteRequestForm(Long requestFormId);
/**
* 撤回申请单(已签发状态撤回至待签发)
*
* @param requestFormId 申请单ID
* @return 结果
*/
R<?> withdrawRequestForm(Long requestFormId);
} }

View File

@@ -95,13 +95,6 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
prescriptionNo = assignSeqUtil.getSeq(AssignSeqEnum.ACTIVITY_PSYCHOTROPIC_NO.getPrefix(), 8); prescriptionNo = assignSeqUtil.getSeq(AssignSeqEnum.ACTIVITY_PSYCHOTROPIC_NO.getPrefix(), 8);
} }
// 诊疗执行科室配置校验(必须在数据库操作之前执行)
List<ActivityOrganizationConfigDto> activityOrganizationConfig =
requestFormManageAppMapper.getActivityOrganizationConfig(typeCode);
if (activityOrganizationConfig.isEmpty()) {
throw new ServiceException("请先配置当前时间段的执行科室");
}
// 当前时间 // 当前时间
Date curDate = new Date(); Date curDate = new Date();
// 请求类型 // 请求类型
@@ -146,6 +139,12 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
// 诊疗集合 // 诊疗集合
List<ActivitySaveDto> activityList = requestFormSaveDto.getActivityList(); List<ActivitySaveDto> activityList = requestFormSaveDto.getActivityList();
log.info("保存申请单typeCode={}, activityListSize={}, encounterId={}", typeCode, activityList != null ? activityList.size() : 0, encounterId); log.info("保存申请单typeCode={}, activityListSize={}, encounterId={}", typeCode, activityList != null ? activityList.size() : 0, encounterId);
// 诊疗执行科室配置
List<ActivityOrganizationConfigDto> activityOrganizationConfig =
requestFormManageAppMapper.getActivityOrganizationConfig(typeCode);
if (activityOrganizationConfig.isEmpty()) {
throw new ServiceException("请先配置当前时间段的执行科室");
}
for (ActivitySaveDto activitySaveDto : activityList) { for (ActivitySaveDto activitySaveDto : activityList) {
serviceRequest = new ServiceRequest(); serviceRequest = new ServiceRequest();
@@ -435,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 =
@@ -457,4 +456,68 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
return requestFormManageAppMapper.getRequestFormPage(requestFormDto, page); return requestFormManageAppMapper.getRequestFormPage(requestFormDto, page);
} }
/**
* 删除申请单(仅待签发状态可删除)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R<?> deleteRequestForm(Long requestFormId) {
if (requestFormId == null) {
return R.fail("申请单ID不能为空");
}
RequestForm requestForm = iRequestFormService.getById(requestFormId);
if (requestForm == null) {
return R.fail("申请单不存在");
}
if (!Integer.valueOf(0).equals(requestForm.getStatus())) {
return R.fail("仅待签发状态的申请单可删除");
}
// 删除申请单
iRequestFormService.removeById(requestFormId);
// 删除关联的诊疗项目及账单
String prescriptionNo = requestForm.getPrescriptionNo();
List<Long> serviceRequestIds = iServiceRequestService
.list(new LambdaQueryWrapper<ServiceRequest>().eq(ServiceRequest::getPrescriptionNo, prescriptionNo))
.stream().map(ServiceRequest::getId).collect(Collectors.toList());
for (Long serviceRequestId : serviceRequestIds) {
iServiceRequestService.removeById(serviceRequestId);
iChargeItemService.deleteByServiceTableAndId(CommonConstants.TableName.WOR_SERVICE_REQUEST, serviceRequestId);
}
return R.ok(null, "删除成功");
}
/**
* 撤回申请单(已签发状态撤回至待签发)
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R<?> withdrawRequestForm(Long requestFormId) {
if (requestFormId == null) {
return R.fail("申请单ID不能为空");
}
RequestForm requestForm = iRequestFormService.getById(requestFormId);
if (requestForm == null) {
return R.fail("申请单不存在");
}
if (!Integer.valueOf(1).equals(requestForm.getStatus())) {
return R.fail("仅已签发状态的申请单可撤回");
}
// 将申请单状态回滚至待签发
RequestForm updateForm = new RequestForm();
updateForm.setId(requestFormId);
updateForm.setStatus(0);
iRequestFormService.updateById(updateForm);
// 将关联的诊疗项目状态回滚至DRAFT
String prescriptionNo = requestForm.getPrescriptionNo();
List<ServiceRequest> serviceRequests = iServiceRequestService
.list(new LambdaQueryWrapper<ServiceRequest>().eq(ServiceRequest::getPrescriptionNo, prescriptionNo));
for (ServiceRequest serviceRequest : serviceRequests) {
ServiceRequest updateService = new ServiceRequest();
updateService.setId(serviceRequest.getId());
updateService.setStatusEnum(RequestStatus.DRAFT.getValue());
iServiceRequestService.updateById(updateService);
}
return R.ok(null, "撤回成功");
}
} }

View File

@@ -81,21 +81,14 @@ public class RequestFormManageController {
* 查询检查申请单 * 查询检查申请单
* *
* @param encounterId 就诊id * @param encounterId 就诊id
* @param startDate 开始日期可选格式yyyy-MM-dd
* @param endDate 结束日期可选格式yyyy-MM-dd
* @param status 单据状态(可选)
* @return 检查申请单 * @return 检查申请单
*/ */
@GetMapping(value = "/get-check") @GetMapping(value = "/get-check")
public R<?> getCheckRequestForm( public R<?> getCheckRequestForm(@RequestParam(required = false) Long encounterId) {
@RequestParam(required = false) Long encounterId,
@RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate,
@RequestParam(required = false) String status) {
if (encounterId == null) { if (encounterId == null) {
return R.fail("就诊ID不能为空"); return R.fail("就诊ID不能为空");
} }
return R.ok(iRequestFormManageAppService.getRequestForm(encounterId, ActivityDefCategory.TEST.getCode(), startDate, endDate, status)); return R.ok(iRequestFormManageAppService.getRequestForm(encounterId, ActivityDefCategory.TEST.getCode()));
} }
/** /**
@@ -105,6 +98,7 @@ public class RequestFormManageController {
* @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 检验申请单
*/ */
@GetMapping(value = "/get-inspection") @GetMapping(value = "/get-inspection")
@@ -112,11 +106,12 @@ public class RequestFormManageController {
@RequestParam(required = false) Long encounterId, @RequestParam(required = false) Long encounterId,
@RequestParam(required = false) String startDate, @RequestParam(required = false) String startDate,
@RequestParam(required = false) String endDate, @RequestParam(required = false) String endDate,
@RequestParam(required = false) String status) { @RequestParam(required = false) String status,
@RequestParam(required = false) String keyword) {
if (encounterId == null) { if (encounterId == null) {
return R.fail("就诊ID不能为空"); return R.fail("就诊ID不能为空");
} }
return R.ok(iRequestFormManageAppService.getRequestForm(encounterId, ActivityDefCategory.PROOF.getCode(), startDate, endDate, status)); return R.ok(iRequestFormManageAppService.getRequestForm(encounterId, ActivityDefCategory.PROOF.getCode(), startDate, endDate, status, keyword));
} }
/** /**

View File

@@ -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);
/** /**
* 查询申请单详情 * 查询申请单详情

View File

@@ -280,9 +280,17 @@
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 LEFT JOIN (
SELECT med_req_id, status_enum
FROM (
SELECT med_req_id, status_enum,
ROW_NUMBER() OVER (PARTITION BY med_req_id ORDER BY id DESC) AS rn
FROM med_medication_dispense
WHERE delete_flag = '0'
) t
WHERE rn = 1
) mmd
ON mmd.med_req_id = T1.id 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}

View File

@@ -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;
}
}

View File

@@ -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()));
} }
} }

View File

@@ -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. 查询号源槽位信息

View File

@@ -59,4 +59,9 @@ public class RequestForm extends HisBaseEntity {
*/ */
private String typeCode; private String typeCode;
/**
* 单据状态 0=待签发 1=已签发 2=已校对 3=待接收 4=已接收 5=已检查 6=已出报告 7=已作废
*/
private Integer status;
} }

View File

@@ -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()

View File

@@ -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">

View File

@@ -522,6 +522,7 @@ const categoryList = ref([]); // 原始分类+项目数据
const dictSearchKey = ref(''); const dictSearchKey = ref('');
const activeNames = ref(''); // 当前展开的折叠项 const activeNames = ref(''); // 当前展开的折叠项
const categoryLoadingSet = ref(new Set()); // Bug #500: 正在加载方法的分类集合 const categoryLoadingSet = ref(new Set()); // Bug #500: 正在加载方法的分类集合
const isAnimating = ref(false); // Bug #500: 防止快速切换时折叠动画重叠导致抖动
const allMethods = ref([]); const allMethods = ref([]);
@@ -669,8 +670,13 @@ async function handleCategoryExpand(cat) {
categoryLoadingSet.value.delete(cat.typeId); categoryLoadingSet.value.delete(cat.typeId);
} }
} }
// Bug #500: 改为非 async 函数,避免 el-collapse 的 @change 等待异步完成导致抖动 // Bug #500: 添加防抖逻辑,快速切换时跳过中间状态的动画,避免高度跳变和白屏闪烁
function handleCollapseChange(activeName) { 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)) {
@@ -1307,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;
@@ -1479,10 +1486,10 @@ defineExpose({ getList });
padding-bottom: 4px; padding-bottom: 4px;
transition: all 0.3s ease; transition: all 0.3s ease;
} }
/* Bug #500: 折叠面板展开/收起动画使用 will-change 优化性能 */ /* Bug #500: 折叠面板动画容器,添加 overflow:hidden 防止展开时内容溢出导致闪烁 */
:deep(.el-collapse-item__wrap) { :deep(.el-collapse-item__wrap) {
border: none; border: none;
will-change: height; overflow: hidden;
} }
:deep(.el-collapse-item) { :deep(.el-collapse-item) {
transition: margin 0.2s ease; transition: margin 0.2s ease;

View File

@@ -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 = [];
} }

View File

@@ -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>
@@ -86,62 +77,18 @@
</template> </template>
<el-table-column type="index" label="序号" width="60" align="center" /> <el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="patientName" label="患者姓名" width="120" /> <el-table-column prop="patientName" label="患者姓名" width="120" />
<el-table-column label="申请单名称" width="140"> <el-table-column prop="name" label="申请单名称" width="140" />
<template #default="scope">
<span>{{ getItemName(scope.row) }}</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="160" /> <el-table-column prop="createTime" label="创建时间" width="160" />
<el-table-column prop="prescriptionNo" label="申请单号" width="140" /> <el-table-column prop="prescriptionNo" label="处方号" width="140" />
<el-table-column prop="requesterId_dictText" label="申请者" width="120" /> <el-table-column prop="requesterId_dictText" label="申请者" width="120" />
<el-table-column label="申请单状态" width="120" align="center"> <el-table-column label="申请单状态" width="120" align="center">
<template #default="scope"> <template #default="scope">
<span>{{ parseStatus(scope.row.status) }}</span> <span>{{ parseStatus(scope.row.status) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center" fixed="right" width="240"> <el-table-column label="操作" align="center" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button> <el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
<!-- 待签发修改删除 -->
<template v-if="scope.row.status === '0' || scope.row.status === 0">
<el-button link type="primary" @click="handleEdit(scope.row)">修改</el-button>
<el-popconfirm
title="确认删除该申请单?"
@confirm="handleDelete(scope.row)"
width="200"
>
<template #reference>
<el-button link type="danger">删除</el-button>
</template>
</el-popconfirm>
</template>
<!-- 已签发护士未校对撤回 -->
<template v-else-if="scope.row.status === '1' || scope.row.status === 1">
<el-popconfirm
title="确认撤回该申请单?撤回后将回滚至待签发状态"
@confirm="handleWithdraw(scope.row)"
width="260"
>
<template #reference>
<el-button link type="warning">撤回</el-button>
</template>
</el-popconfirm>
</template>
<!-- 已校对/待接收打印 -->
<template v-else-if="scope.row.status === '2' || scope.row.status === 2 || scope.row.status === '3' || scope.row.status === 3">
<el-button link type="primary" @click="handlePrint(scope.row)">打印</el-button>
</template>
<!-- 已接收/已检查打印看报告 -->
<template v-else-if="scope.row.status === '4' || scope.row.status === 4 || scope.row.status === '5' || scope.row.status === 5">
<el-button link type="primary" @click="handlePrint(scope.row)">打印</el-button>
<el-button link type="primary" @click="handleViewReport(scope.row)">看报告</el-button>
</template>
<!-- 已出报告打印看报告 -->
<template v-else-if="scope.row.status === '6' || scope.row.status === 6">
<el-button link type="primary" @click="handlePrint(scope.row)">打印</el-button>
<el-button link type="success" @click="handleViewReport(scope.row)">看报告</el-button>
</template>
<!-- 已作废仅详情 -->
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -163,7 +110,7 @@
currentDetail.patientName || '-' currentDetail.patientName || '-'
}}</el-descriptions-item> }}</el-descriptions-item>
<el-descriptions-item label="申请单名称">{{ <el-descriptions-item label="申请单名称">{{
getItemName(currentDetail) currentDetail.name || '-'
}}</el-descriptions-item> }}</el-descriptions-item>
<el-descriptions-item label="申请单状态">{{ <el-descriptions-item label="申请单状态">{{
parseStatus(currentDetail.status) parseStatus(currentDetail.status)
@@ -171,7 +118,7 @@
<el-descriptions-item label="创建时间">{{ <el-descriptions-item label="创建时间">{{
currentDetail.createTime || '-' currentDetail.createTime || '-'
}}</el-descriptions-item> }}</el-descriptions-item>
<el-descriptions-item label="申请单号">{{ <el-descriptions-item label="处方号">{{
currentDetail.prescriptionNo || '-' currentDetail.prescriptionNo || '-'
}}</el-descriptions-item> }}</el-descriptions-item>
<el-descriptions-item label="申请者">{{ <el-descriptions-item label="申请者">{{
@@ -213,101 +160,6 @@
<el-button @click="detailDialogVisible = false">关闭</el-button> <el-button @click="detailDialogVisible = false">关闭</el-button>
</template> </template>
</el-dialog> </el-dialog>
<!-- 修改申请单弹窗 -->
<el-dialog
v-model="editDialogVisible"
title="修改检查申请"
width="800px"
destroy-on-close
top="5vh"
:close-on-click-modal="false"
>
<div v-if="editDetail" class="applicationShow-container">
<el-form :model="editForm" label-width="120px">
<el-form-item label="申请单名称">
<el-input v-model="editForm.name" />
</el-form-item>
<el-form-item label="发往科室">
<el-tree-select
clearable
style="width: 100%"
v-model="editForm.targetDepartment"
filterable
:data="orgOptions"
:props="{
value: 'id',
label: 'name',
children: 'children',
}"
value-key="id"
check-strictly
placeholder="请选择科室"
/>
</el-form-item>
<el-form-item label="紧急程度">
<el-select v-model="editForm.urgencyLevel" placeholder="请选择紧急程度" clearable style="width: 100%">
<el-option label="普通" value="routine" />
<el-option label="急诊" value="emergency" />
</el-select>
</el-form-item>
<el-form-item label="过敏史">
<el-input v-model="editForm.allergyHistory" type="textarea" :rows="2" />
</el-form-item>
<el-form-item label="检查目的">
<el-input v-model="editForm.examinationPurpose" type="textarea" :rows="2" />
</el-form-item>
<el-form-item label="病史摘要">
<el-input v-model="editForm.medicalHistorySummary" type="textarea" :rows="2" />
</el-form-item>
<el-form-item label="期望检查时间">
<el-date-picker
v-model="editForm.expectedExaminationTime"
type="datetime"
placeholder="请选择期望检查时间"
style="width: 100%"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<el-form-item label="临床诊断">
<el-input v-model="editForm.clinicalDiagnosis" type="textarea" :rows="2" />
</el-form-item>
<el-form-item label="注意事项">
<el-input v-model="editForm.attention" type="textarea" :rows="2" />
</el-form-item>
<div v-if="editDetail.requestFormDetailList && editDetail.requestFormDetailList.length">
<el-divider>申请项目</el-divider>
<el-table :data="editDetail.requestFormDetailList" border size="small">
<el-table-column type="index" label="序号" width="60" align="center" />
<el-table-column prop="adviceName" label="医嘱名称" />
<el-table-column prop="quantity" label="数量" width="80" align="center" />
<el-table-column prop="unitCode_dictText" label="单位" width="100" />
<el-table-column prop="totalPrice" label="总价" width="100" align="right" />
</el-table>
</div>
</el-form>
</div>
<template #footer>
<el-button @click="editDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSaveEdit" :loading="saveLoading">保存</el-button>
</template>
</el-dialog>
<!-- 报告查看弹窗 -->
<el-dialog
v-model="reportDialogVisible"
title="检查报告"
width="900px"
destroy-on-close
top="5vh"
:close-on-click-modal="false"
>
<div v-if="reportUrl" style="min-height: 500px;">
<iframe :src="reportUrl" style="width: 100%; height: 500px; border: none;" />
</div>
<el-empty v-else description="暂无报告数据" />
<template #footer>
<el-button @click="reportDialogVisible = false">关闭</el-button>
</template>
</el-dialog>
</div> </div>
</template> </template>
@@ -315,7 +167,7 @@
import {computed, getCurrentInstance, ref, watch} from 'vue'; 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 {getCheck, deleteRequestForm, withdrawRequestForm, getTestResult} from './api'; import {getCheck} from './api';
import {getOrgList} from '@/views/doctorstation/components/api.js'; import {getOrgList} from '@/views/doctorstation/components/api.js';
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
@@ -327,33 +179,10 @@ const currentDetail = ref(null);
const descJsonData = ref(null); const descJsonData = ref(null);
const orgOptions = ref([]); const orgOptions = ref([]);
// 修改申请单相关
const editDialogVisible = ref(false);
const editDetail = ref(null);
const editForm = ref({});
const saveLoading = ref(false);
// 报告查看相关
const reportDialogVisible = ref(false);
const reportUrl = 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 () => {
@@ -378,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;
@@ -419,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();
}; };
@@ -444,36 +267,9 @@ const parseStatus = (status) => {
return statusMap[String(status)] || '-'; return statusMap[String(status)] || '-';
}; };
/**
* 获取申请单展示名称:优先使用具体医嘱名称列表
*/
const getItemName = (row) => {
const items = row?.requestFormDetailList;
if (items && items.length > 0) {
return items.map(item => item.adviceName).filter(Boolean).join('、') || row.name || '-';
}
return row.name || '-';
};
/**
* 构建打印用项目名称字符串(同步函数,用于模板字符串)
*/
const buildItemNames = (row) => {
const items = row?.requestFormDetailList;
if (items && items.length > 0) {
return items.map(item => item.adviceName).filter(Boolean).join('、') || row.name || '';
}
return row.name || '';
};
const labelMap = { const labelMap = {
categoryType: '项目类别', categoryType: '项目类别',
targetDepartment: '发往科室', targetDepartment: '发往科室',
urgencyLevel: '紧急程度',
allergyHistory: '过敏史',
examinationPurpose: '检查目的',
medicalHistorySummary: '病史摘要',
expectedExaminationTime: '期望检查时间',
symptom: '症状', symptom: '症状',
sign: '体征', sign: '体征',
clinicalDiagnosis: '临床诊断', clinicalDiagnosis: '临床诊断',
@@ -503,26 +299,19 @@ const getLocationInfo = () => {
}; };
const recursionFun = (targetDepartment) => { const recursionFun = (targetDepartment) => {
if (!targetDepartment) return '';
if (!Array.isArray(orgOptions.value) || orgOptions.value.length === 0) return '';
let name = ''; let name = '';
for (let index = 0; index < orgOptions.value.length; index++) { for (let index = 0; index < orgOptions.value.length; index++) {
const obj = orgOptions.value[index]; const obj = orgOptions.value[index];
if (obj.id == targetDepartment) { if (obj.id == targetDepartment) {
name = obj.name; name = obj.name;
break;
} }
const subObjArray = obj['children']; const subObjArray = obj['children'];
if (Array.isArray(subObjArray)) { for (let index = 0; index < subObjArray.length; index++) {
for (let i = 0; i < subObjArray.length; i++) { const item = subObjArray[index];
const item = subObjArray[i]; if (item.id == targetDepartment) {
if (item.id == targetDepartment) { name = item.name;
name = item.name;
break;
}
} }
} }
if (name) break;
} }
return name; return name;
}; };
@@ -535,15 +324,7 @@ const handleViewDetail = (row) => {
if (row.descJson) { if (row.descJson) {
try { try {
const obj = JSON.parse(row.descJson); const obj = JSON.parse(row.descJson);
if (obj.targetDepartment) { obj.targetDepartment = recursionFun(obj.targetDepartment);
const deptName = recursionFun(obj.targetDepartment);
obj.targetDepartment = deptName || obj.targetDepartment;
}
// 紧急程度转换为显示文本
const urgencyMap = { routine: '普通', emergency: '急诊' };
if (obj.urgencyLevel) {
obj.urgencyLevel = urgencyMap[obj.urgencyLevel] || obj.urgencyLevel;
}
descJsonData.value = obj; descJsonData.value = obj;
} catch (e) { } catch (e) {
console.error('解析 descJson 失败:', e); console.error('解析 descJson 失败:', e);
@@ -555,229 +336,6 @@ const handleViewDetail = (row) => {
detailDialogVisible.value = true; detailDialogVisible.value = true;
}; };
/**
* 修改申请单
*/
const handleEdit = (row) => {
editDetail.value = row;
// 解析 descJson 为表单数据
const form = { name: row.name || '' };
if (row.descJson) {
try {
const desc = JSON.parse(row.descJson);
form.targetDepartment = desc.targetDepartment || '';
form.urgencyLevel = desc.urgencyLevel || '';
form.allergyHistory = desc.allergyHistory || '';
form.examinationPurpose = desc.examinationPurpose || '';
form.medicalHistorySummary = desc.medicalHistorySummary || '';
form.expectedExaminationTime = desc.expectedExaminationTime || '';
form.clinicalDiagnosis = desc.clinicalDiagnosis || '';
form.attention = desc.attention || '';
form.symptom = desc.symptom || '';
form.sign = desc.sign || '';
} catch (e) {
console.error('解析 descJson 失败:', e);
}
}
editForm.value = form;
editDialogVisible.value = true;
};
/**
* 保存修改
*/
const handleSaveEdit = async () => {
if (!editDetail.value?.requestFormId) {
proxy.$modal?.msgWarning?.('申请单ID不存在');
return;
}
saveLoading.value = true;
try {
// 复用 save-check 接口进行更新(传入 requestFormId 即为编辑场景)
const res = await proxy.$http?.post?.('/reg-doctorstation/request-form/save-check', {
requestFormId: editDetail.value.requestFormId,
encounterId: editDetail.value.encounterId,
patientId: editDetail.value.patientId,
name: editForm.value.name,
organizationId: editDetail.value.inHospitalOrgId || patientInfo.value?.inHospitalOrgId,
descJson: JSON.stringify({
targetDepartment: editForm.value.targetDepartment,
urgencyLevel: editForm.value.urgencyLevel,
allergyHistory: editForm.value.allergyHistory,
examinationPurpose: editForm.value.examinationPurpose,
medicalHistorySummary: editForm.value.medicalHistorySummary,
expectedExaminationTime: editForm.value.expectedExaminationTime,
clinicalDiagnosis: editForm.value.clinicalDiagnosis,
attention: editForm.value.attention,
symptom: editForm.value.symptom,
sign: editForm.value.sign,
}),
activityList: [], // 修改场景仅更新描述信息,不修改项目
});
if (res?.code === 200) {
proxy.$modal?.msgSuccess?.('修改成功');
editDialogVisible.value = false;
await fetchData();
} else {
proxy.$modal?.msgError?.(res?.msg || '修改失败');
}
} catch (e) {
console.warn('修改申请单失败:', e.message);
proxy.$modal?.msgError?.('修改失败: ' + (e.message || '网络异常'));
} finally {
saveLoading.value = false;
}
};
/**
* 删除申请单(仅待签发状态可删除)
*/
const handleDelete = async (row) => {
if (!row.requestFormId) {
proxy.$modal?.msgWarning?.('申请单ID不存在');
return;
}
try {
const res = await deleteRequestForm({ requestFormId: row.requestFormId });
if (res?.code === 200) {
proxy.$modal?.msgSuccess?.('删除成功');
await fetchData();
} else {
proxy.$modal?.msgError?.(res?.msg || '删除失败');
}
} catch (e) {
console.warn('删除申请单失败:', e.message);
proxy.$modal?.msgError?.('删除失败: ' + (e.message || '网络异常'));
}
};
/**
* 撤回申请单(已签发状态撤回至待签发)
*/
const handleWithdraw = async (row) => {
if (!row.requestFormId) {
proxy.$modal?.msgWarning?.('申请单ID不存在');
return;
}
try {
const res = await withdrawRequestForm({ requestFormId: row.requestFormId });
if (res?.code === 200) {
proxy.$modal?.msgSuccess?.('撤回成功');
await fetchData();
} else {
proxy.$modal?.msgError?.(res?.msg || '撤回失败');
}
} catch (e) {
console.warn('撤回申请单失败:', e.message);
proxy.$modal?.msgError?.('撤回失败: ' + (e.message || '网络异常'));
}
};
/**
* 打印申请单
*/
const handlePrint = (row) => {
// 打开新窗口进行打印
const printWindow = window.open('', '_blank');
if (!printWindow) {
proxy.$modal?.msgWarning?.('请允许浏览器弹出窗口以进行打印');
return;
}
const content = buildPrintContent(row);
printWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>检查申请单打印</title>
<style>
body { font-family: "Microsoft YaHei", sans-serif; padding: 20px; font-size: 14px; }
h2 { text-align: center; margin-bottom: 20px; }
table { width: 100%; border-collapse: collapse; margin-top: 10px; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background: #f5f5f5; }
.info-row { margin: 8px 0; }
.label { font-weight: bold; display: inline-block; width: 100px; }
@media print { .no-print { display: none; } }
</style>
</head>
<body>
${content}
<div class="no-print" style="text-align: center; margin-top: 20px;">
<button onclick="window.print()">打印</button>
<button onclick="window.close()">关闭</button>
</div>
</body>
</html>
`);
printWindow.document.close();
};
/**
* 构建打印内容
*/
const buildPrintContent = (row) => {
let descHtml = '';
if (row.descJson) {
try {
const desc = JSON.parse(row.descJson);
descHtml = Object.entries(labelMap)
.filter(([key]) => desc[key])
.map(([key, label]) => `<div class="info-row"><span class="label">${label}:</span>${desc[key]}</div>`)
.join('');
} catch (e) {
console.error('解析 descJson 失败:', e);
}
}
let itemsHtml = '';
if (row.requestFormDetailList && row.requestFormDetailList.length) {
itemsHtml = `
<h3>申请项目</h3>
<table>
<tr><th>序号</th><th>医嘱名称</th><th>数量</th><th>单位</th><th>总价</th></tr>
${row.requestFormDetailList.map((item, i) => `
<tr>
<td>${i + 1}</td>
<td>${item.adviceName || '-'}</td>
<td>${item.quantity || '-'}</td>
<td>${item.unitCode_dictText || '-'}</td>
<td>${item.totalPrice || '-'}</td>
</tr>
`).join('')}
</table>
`;
}
return `
<h2>检查申请单</h2>
<div class="info-row"><span class="label">患者姓名:</span>${row.patientName || '-'}</div>
<div class="info-row"><span class="label">申请单名称:</span>${buildItemNames(row) || '-'}</div>
<div class="info-row"><span class="label">创建时间:</span>${row.createTime || '-'}</div>
<div class="info-row"><span class="label">申请单号:</span>${row.prescriptionNo || '-'}</div>
<div class="info-row"><span class="label">申请者:</span>${row.requesterId_dictText || '-'}</div>
<div class="info-row"><span class="label">申请单状态:</span>${parseStatus(row.status)}</div>
${descHtml ? `<h3>申请单描述</h3>${descHtml}` : ''}
${itemsHtml}
`;
};
/**
* 查看报告
*/
const handleViewReport = async (row) => {
reportUrl.value = '';
reportDialogVisible.value = true;
try {
const res = await getTestResult({ encounterId: row.encounterId, requestFormId: row.requestFormId });
if (res?.code === 200 && res?.data) {
reportUrl.value = res.data;
} else {
console.warn('获取检查报告失败:', res?.msg || '无数据');
}
} catch (e) {
console.warn('获取检查报告异常:', e.message);
}
};
watch( watch(
() => patientInfo.value?.encounterId, () => patientInfo.value?.encounterId,
(val) => { (val) => {
@@ -786,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 }

View File

@@ -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 = [];
} }

View File

@@ -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>
@@ -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 () => {
getDepartmentList().then((res) => { const res = await getDepartmentList();
orgOptions.value = res.data || []; orgOptions.value = res.data || [];
});
}; };
const recursionFun = (targetDepartment) => { const recursionFun = (targetDepartment) => {
@@ -357,7 +372,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) {
@@ -378,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();
@@ -398,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 }

View File

@@ -881,8 +881,9 @@ function handleFocus(row, index) {
// 用 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);
// 修复Bug #486当行没有显式选择医嘱类型时不传categoryCode让搜索在全药库中进行 // 修复Bug #486当行没有显式选择医嘱类型时row.adviceType为undefined
const categoryCode = selectedItem ? selectedItem.categoryCode : (row.adviceType !== undefined ? (adviceQueryParams.value.categoryCode || '') : ''); // 不传categoryCode让搜索在全药库中进行只有行已选择类型时才用对应categoryCode过滤
const categoryCode = row.adviceType !== undefined ? (selectedItem ? selectedItem.categoryCode : '') : '';
const searchKey = row.adviceName || ''; const searchKey = row.adviceName || '';
nextTick(() => { nextTick(() => {
@@ -919,8 +920,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);
// 修复Bug #486当行没有显式选择医嘱类型时不传categoryCode让搜索在全药库中进行 // 修复Bug #486当行没有显式选择医嘱类型时row?.adviceType为undefined
const categoryCode = selectedItem ? selectedItem.categoryCode : (row?.adviceType !== undefined ? (adviceQueryParams.value.categoryCode || '') : ''); // 不传categoryCode让搜索在全药库中进行只有行已选择类型时才用对应categoryCode过滤
const categoryCode = row?.adviceType !== undefined ? (selectedItem ? selectedItem.categoryCode : '') : '';
tableRef.refresh(adviceType, categoryCode, value); tableRef.refresh(adviceType, categoryCode, value);
} }
} }

View File

@@ -17,11 +17,13 @@
</el-tab-pane> </el-tab-pane>
<!-- <el-tab-pane label="医技报告" name="fourth">Task</el-tab-pane> --> <!-- <el-tab-pane label="医技报告" name="fourth">Task</el-tab-pane> -->
<el-tab-pane label="检验申请" name="test"> <el-tab-pane label="检验申请" name="test">
<TestApplication ref="testApplicationRef" :show-status-column="false" /> <TestApplication ref="testApplicationRef" :show-status-column="true" />
</el-tab-pane> </el-tab-pane>
```vue
<el-tab-pane label="检查申请" name="examine"> <el-tab-pane label="检查申请" name="examine">
<ExamineApplication ref="examineApplicationRef" /> <ExamineApplication ref="examineApplicationRef" />
</el-tab-pane> </el-tab-pane>
```
<el-tab-pane label="汇总发药申请" name="summaryDrug"> <el-tab-pane label="汇总发药申请" name="summaryDrug">
<SummaryDrugApplication ref="summaryDrugApplicationRef" /> <SummaryDrugApplication ref="summaryDrugApplicationRef" />
</el-tab-pane> </el-tab-pane>
@@ -44,11 +46,10 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import { computed, onBeforeMount, onMounted, provide, reactive, ref, watch } from 'vue'; import {computed, onBeforeMount, onMounted, provide, reactive, ref, watch,} from 'vue';
import Emr from './emr/index.vue'; import Emr from './emr/index.vue';
import inPatientBarDoctorFold from '@/components/patientBar/inPatientBarDoctorFold.vue'; import inPatientBarDoctorFold from '@/components/patientBar/inPatientBarDoctorFold.vue';
import SummaryDrugApplication from './components/applicationShow/summaryDrugApplication.vue';
import PatientList from '@/components/PatientList/patient-list.vue'; import PatientList from '@/components/PatientList/patient-list.vue';
import {localPatientInfo, updateLocalPatientInfo} from './store/localPatient'; import {localPatientInfo, updateLocalPatientInfo} from './store/localPatient';
import {patientInfo, updatePatientInfo} from './store/patient'; import {patientInfo, updatePatientInfo} from './store/patient';
@@ -83,7 +84,6 @@ const currentPatientInfo = ref({});
const testApplicationRef = ref(); const testApplicationRef = ref();
const examineApplicationRef = ref(); const examineApplicationRef = ref();
const surgeryApplicationRef = ref(); const surgeryApplicationRef = ref();
const summaryDrugApplicationRef = ref();
const bloodTtransfusionAapplicationRef = ref(); const bloodTtransfusionAapplicationRef = ref();
// 患者列表相关逻辑 // 患者列表相关逻辑

View File

@@ -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,
}) })

View File

@@ -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();
} }

View File

@@ -469,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();
lotNumberMatch({ encounterIdList: encounterIds }, { skipErrorMsg: true }).catch((error) => { // 仅当选中医嘱中包含药品/耗材类医嘱时,才调用耗材批号匹配
console.warn('lotNumberMatch failed after adviceExecute:', error); const hasMedOrDevice = list.some(
}); (item) =>
item.adviceTable === 'med_medication_request' ||
String(item.adviceTable || '').includes('device'),
);
if (hasMedOrDevice) {
lotNumberMatch({ encounterIdList: encounterIds }, { skipErrorMsg: true }).catch((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 || '医嘱执行失败');
} }

View File

@@ -198,53 +198,52 @@
</el-button> </el-button>
</div> </div>
<div class="queue-actions-right">
<el-button
:type="showOnlyWaiting ? 'primary' : ''"
size="small"
@click="showOnlyWaiting = true"
>
只显示等待
</el-button>
<el-button
:type="!showOnlyWaiting ? 'primary' : ''"
size="small"
@click="showOnlyWaiting = false"
>
显示全部状态
</el-button>
</div>
</div> </div>
</div> </div>
</div> </div>
<!-- 底部控制面板 --> <!-- 底部控制面板 -->
<div class="footer-section"> <div class="footer-section">
<!-- 诊室快速过滤栏 --> <!-- Bug #411诊室快速过滤栏筛选维度从科室改为诊室 -->
<div class="filter-section"> <div class="filter-section">
<div class="filter-label"> <div class="filter-left">
诊室快速过滤栏 <div class="filter-label">
</div> 诊室快速过滤栏
<div class="filter-select-wrapper"> </div>
<el-select <div class="filter-button-wrapper">
v-model="selectedClinicRoom" <el-button
placeholder="请选择诊室" :type="selectedRoom === 'all' ? 'primary' : ''"
clearable size="small"
filterable @click="selectedRoom = 'all'"
style="width: 100%" >
size="default" 全部
> </el-button>
<el-option <el-button
label="全部" v-for="room in uniqueRooms"
value="all"
/>
<el-option
v-for="room in clinicRoomList"
:key="room" :key="room"
:label="room" :type="selectedRoom === room ? 'primary' : ''"
:value="room" size="small"
/> @click="selectedRoom = room"
</el-select> >
{{ room }}
</el-button>
</div>
</div>
<div class="filter-right">
<el-button
:type="showOnlyWaiting ? 'primary' : ''"
size="small"
@click="showOnlyWaiting = true"
>
只显示等待
</el-button>
<el-button
:type="!showOnlyWaiting ? 'primary' : ''"
size="small"
@click="showOnlyWaiting = false"
>
显示全部状态
</el-button>
</div> </div>
</div> </div>
@@ -680,10 +679,8 @@ const selectedCandidates = ref([])
// 显示选项 // 显示选项
const showOnlyWaiting = ref(false) const showOnlyWaiting = ref(false)
// 诊室过滤(按诊室维度筛选 // Bug #411诊室过滤替代原来的科室下拉框selectedDept/departmentList 已移除
const selectedClinicRoom = ref('all') const selectedRoom = ref('all')
// 诊室列表(从数据中动态提取)
const clinicRoomList = ref([])
// 修复【#397】动态获取当前科室名称 // 修复【#397】动态获取当前科室名称
const currentDeptName = computed(() => { const currentDeptName = computed(() => {
@@ -906,11 +903,12 @@ const mapFrontendStatusToBackend = (status) => {
// 从数据库加载队列 // 从数据库加载队列
const loadQueueFromDb = async () => { const loadQueueFromDb = async () => {
try { try {
// 使用当前登录人科室 // Bug #411不再按科室选筛加载后端默认按当前登录人科室查询
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 开始date=', todayStr) const res = await getTriageQueueList({ organizationId, date: todayStr }).catch((err) => {
const res = await getTriageQueueList({ 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 }
}) })
@@ -1138,8 +1136,6 @@ const loadDataFromApi = async () => {
// 同步当前呼叫(队列从 DB 加载后已同步;这里再兜底一次) // 同步当前呼叫(队列从 DB 加载后已同步;这里再兜底一次)
syncCurrentCallFromQueue() syncCurrentCallFromQueue()
// 提取诊室列表供过滤栏使用
extractClinicRooms()
console.log('【心内科】数据加载完成:候选池', originalCandidatePoolList.value.length, '条,队列', originalQueueList.value.length, '条') console.log('【心内科】数据加载完成:候选池', originalCandidatePoolList.value.length, '条,队列', originalQueueList.value.length, '条')
ElMessage.success('【心内科】已从门诊挂号接口加载数据') ElMessage.success('【心内科】已从门诊挂号接口加载数据')
} catch (e) { } catch (e) {
@@ -1151,42 +1147,35 @@ const loadDataFromApi = async () => {
totalSignedIn.value = originalCandidatePoolList.value.length totalSignedIn.value = originalCandidatePoolList.value.length
totalInQueue.value = originalQueueList.value.length totalInQueue.value = originalQueueList.value.length
syncCurrentCallFromQueue() syncCurrentCallFromQueue()
extractClinicRooms()
} }
} }
// 原始数据存储(用于过滤) // 原始数据存储(用于过滤)
const originalCandidatePoolList = ref(getInitialCandidatePoolList()) const originalCandidatePoolList = ref(getInitialCandidatePoolList())
// 提取诊室列表(从队列和候选池数据中动态获取 // 过滤后的智能候选池数据(按诊室过滤
const extractClinicRooms = () => {
const roomSet = new Set()
// 从队列中提取
originalQueueList.value.forEach(item => {
if (item.room && item.room !== '-') {
roomSet.add(item.room)
}
})
// 从候选池中提取
originalCandidatePoolList.value.forEach(item => {
if (item.room && item.room !== '-') {
roomSet.add(item.room)
}
})
clinicRoomList.value = Array.from(roomSet).sort()
}
// 过滤后的智能候选池数据
const filteredCandidatePoolList = computed(() => { const filteredCandidatePoolList = computed(() => {
if (selectedClinicRoom.value === 'all') { if (selectedRoom.value === 'all') {
return originalCandidatePoolList.value return originalCandidatePoolList.value
} }
return originalCandidatePoolList.value.filter(item => item.room === selectedClinicRoom.value) return originalCandidatePoolList.value.filter(item => item.room === selectedRoom.value)
}) })
// 原始队列数据存储(用于过滤) // 原始队列数据存储(用于过滤)
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(':')
@@ -1203,7 +1192,7 @@ 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
@@ -1211,8 +1200,8 @@ const filteredQueueList = computed(() => {
filtered = filtered.filter(item => item.status !== '已完成') filtered = filtered.filter(item => item.status !== '已完成')
// 再按诊室过滤 // 再按诊室过滤
if (selectedClinicRoom.value !== 'all') { if (selectedRoom.value !== 'all') {
filtered = filtered.filter(item => item.room === selectedClinicRoom.value) filtered = filtered.filter(item => item.room === selectedRoom.value)
} }
// 再按状态过滤(只显示等待) // 再按状态过滤(只显示等待)
@@ -1723,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改为从队列数据中动态获取科室
// 全科模式:优先用"当前叫号中/第一个等待"所在科室 const calling = originalQueueList.value.find((i) => i.status === '叫号中')
let orgId = null const waiting = originalQueueList.value.find((i) => i.status === '等待')
{ console.log('【心内科】handleNextPatient 查找:叫号中=', calling?.patientName, '等待=', waiting?.patientName)
const calling = originalQueueList.value.find((i) => i.status === '叫号中') const orgId = calling?.organizationId ?? waiting?.organizationId
const waiting = originalQueueList.value.find((i) => i.status === '等待') console.log('【心内科】handleNextPatient 确定的 orgId=', orgId)
console.log('【心内科】handleNextPatient 查找:叫号中=', calling?.patientName, '等待=', waiting?.patientName)
orgId = calling?.organizationId ?? waiting?.organizationId
console.log('【心内科】handleNextPatient 确定的 orgId=', orgId)
}
if (orgId != null) { if (orgId != null) {
reqData.organizationId = orgId reqData.organizationId = orgId
} }
@@ -1761,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 {
// 如果没有选中患者,使用查询条件(兼容旧逻辑) // 如果没有选中患者,使用当前叫号中的科室
// 全科模式:优先用”当前叫号中”所在科室 const calling = originalQueueList.value.find((i) => i.status === '叫号中')
let orgId = null const orgId = calling?.organizationId
{
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
orgId = calling?.organizationId
}
if (orgId != null) { if (orgId != null) {
reqData.organizationId = orgId reqData.organizationId = orgId
} }
@@ -1795,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 {
// 如果没有选中患者,使用查询条件(兼容旧逻辑) // 如果没有选中患者,使用当前叫号中的科室
// 全科模式:优先用”当前叫号中”所在科室 const calling = originalQueueList.value.find((i) => i.status === '叫号中')
let orgId = null const orgId = calling?.organizationId
{
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
orgId = calling?.organizationId
}
if (orgId != null) { if (orgId != null) {
reqData.organizationId = orgId reqData.organizationId = orgId
} }
@@ -1829,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 {
// 如果没有选中患者,使用查询条件(兼容旧逻辑) // 如果没有选中患者,使用当前叫号中的科室
// 全科模式:优先用”当前叫号中”所在科室 const calling = originalQueueList.value.find((i) => i.status === '叫号中')
let orgId = null const orgId = calling?.organizationId
{
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
orgId = calling?.organizationId
}
if (orgId != null) { if (orgId != null) {
reqData.organizationId = orgId reqData.organizationId = orgId
} }
@@ -2219,10 +2192,6 @@ onUnmounted(() => {
gap: 10px; gap: 10px;
} }
.queue-actions-right {
display: flex;
gap: 10px;
}
} }
.candidate-actions { .candidate-actions {
@@ -2244,16 +2213,32 @@ onUnmounted(() => {
.filter-section { .filter-section {
margin-bottom: 20px; margin-bottom: 20px;
display: flex;
align-items: flex-start;
justify-content: space-between;
.filter-label { .filter-left {
font-size: 14px; flex: 1;
font-weight: bold;
color: #333; .filter-label {
margin-bottom: 10px; font-size: 14px;
font-weight: bold;
color: #333;
margin-bottom: 10px;
}
.filter-button-wrapper {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
} }
.filter-select-wrapper { .filter-right {
width: 100%; display: flex;
gap: 8px;
flex-shrink: 0;
align-self: flex-end;
} }
} }