Compare commits
125 Commits
764befbf28
...
bug475-fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80695d90f9 | ||
|
|
deb6ade97b | ||
|
|
2d7228ca5d | ||
|
|
a447e55d43 | ||
| 1be0dc2417 | |||
| a4b4d36d93 | |||
|
|
940fad5c7d | ||
|
|
ead3733aac | ||
|
|
28bf385ec2 | ||
|
|
31f7c4f32a | ||
|
|
480663f716 | ||
|
|
cc484d5f10 | ||
|
|
30f8cdbd80 | ||
|
|
f4c6c12ef8 | ||
|
|
8cf98008ae | ||
|
|
062c8d9dee | ||
|
|
ffdfebaacf | ||
|
|
9ed52b7c48 | ||
|
|
fc1ed6c4ce | ||
|
|
818564f5ba | ||
|
|
78adbddfde | ||
|
|
861db6b0f5 | ||
|
|
b7df71fd0b | ||
|
|
5bc8a8e517 | ||
| 0264fa9d58 | |||
| cd12dd7a22 | |||
|
|
bfddf87b2c | ||
|
|
84499d4ec1 | ||
|
|
01c5b62024 | ||
|
|
559821e4d3 | ||
|
|
0dd4c25c12 | ||
|
|
3590a18adc | ||
|
|
b96acc2402 | ||
|
|
e83c35c3f1 | ||
|
|
b5d876be36 | ||
|
|
5539d4cc03 | ||
|
|
982e905990 | ||
|
|
0d46f03e68 | ||
|
|
3cab8306c2 | ||
|
|
0600bbecbc | ||
|
|
ac57ac21e9 | ||
|
|
d38efd15b3 | ||
|
|
c7404e9d3f | ||
|
|
69b28c59f6 | ||
|
|
4e71b861ab | ||
|
|
7c471205a3 | ||
|
|
dc8661c3d0 | ||
|
|
c634551fdb | ||
|
|
c556d51eaf | ||
|
|
9280065c5b | ||
|
|
220982fa34 | ||
|
|
460890e3c2 | ||
|
|
6a0a2ca711 | ||
|
|
fbbe0e9248 | ||
|
|
b6df124e1b | ||
|
|
a0162686f4 | ||
|
|
23561cc9b1 | ||
|
|
cdb676c859 | ||
|
|
1b159cdfab | ||
|
|
01d48c75a5 | ||
|
|
c8fa9b4bf0 | ||
|
|
349c587386 | ||
|
|
656079b27b | ||
| ede6180c97 | |||
|
|
8de1f933e5 | ||
|
|
3f35b4f2bb | ||
| eb3d0ee608 | |||
|
|
dfe300cc1f | ||
|
|
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 | ||
|
|
6451c308c2 | ||
|
|
772ec5537c | ||
|
|
af6494c806 | ||
|
|
2901dafe10 | ||
|
|
ffc2562aea | ||
|
|
5da537f863 | ||
|
|
facbe7cd44 | ||
|
|
283d25642a | ||
|
|
7b17a66214 |
@@ -25,7 +25,7 @@ public class SysTenantController extends BaseController {
|
||||
private ISysTenantService sysTenantService;
|
||||
|
||||
/**
|
||||
* 查询租户分页列表
|
||||
* 查询租户分页列表(只读操作,不限制租户管理权限)
|
||||
*
|
||||
* @param tenantId 租户ID查询
|
||||
* @param tenantCode 租户编码模糊查询
|
||||
@@ -35,7 +35,7 @@ public class SysTenantController extends BaseController {
|
||||
* @param pageSize 每页多少条
|
||||
* @return 租户分页列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:operate')")
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:list')")
|
||||
@GetMapping("/page")
|
||||
public R<IPage<SysTenant>> getTenantPage(@RequestParam(required = false) Integer tenantId,
|
||||
@RequestParam(required = false) String tenantCode, @RequestParam(required = false) String tenantName,
|
||||
@@ -45,19 +45,19 @@ public class SysTenantController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询租户详情
|
||||
* 查询租户详情(只读操作)
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @return 租户分页列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:operate')")
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:list')")
|
||||
@GetMapping("/{tenantId}")
|
||||
public R<SysTenant> getTenantDetail(@PathVariable Integer tenantId) {
|
||||
return R.ok(sysTenantService.getById(tenantId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询租户所属用户分页列表
|
||||
* 查询租户所属用户分页列表(只读操作)
|
||||
*
|
||||
* @param tenantId 租户ID查询
|
||||
* @param userName 用户昵称模糊查询
|
||||
@@ -67,7 +67,7 @@ public class SysTenantController extends BaseController {
|
||||
* @param pageSize 每页多少条
|
||||
* @return 租户所属用户分页列表
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:operate')")
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:list')")
|
||||
@GetMapping("/user/page")
|
||||
public R<IPage<SysUser>> getTenantUserPage(@RequestParam(required = false) Integer tenantId,
|
||||
@RequestParam(required = false) String userName, @RequestParam(required = false) String nickName,
|
||||
@@ -141,14 +141,14 @@ public class SysTenantController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询租户未绑定的用户列表
|
||||
* 查询租户未绑定的用户列表(只读操作)
|
||||
*
|
||||
* @param tenantId 租户ID
|
||||
* @param pageNum 当前页
|
||||
* @param pageSize 每页多少条
|
||||
* @return 结果
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:operate')")
|
||||
@PreAuthorize("@ss.hasPermi('system:tenant:list')")
|
||||
@GetMapping("/{tenantId}/unbind-users")
|
||||
public R<IPage<SysUser>> getUnbindTenantUserList(@PathVariable Integer tenantId,
|
||||
@RequestParam(required = false) String userName, @RequestParam(required = false) String nickName,
|
||||
@@ -194,4 +194,4 @@ public class SysTenantController extends BaseController {
|
||||
public R<List<SysTenant>> getUserBindTenantList(@PathVariable String username) {
|
||||
return sysTenantService.getUserBindTenantList(username);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import com.openhis.clinical.service.ITicketService;
|
||||
import com.openhis.web.appointmentmanage.appservice.ITicketAppService;
|
||||
import com.openhis.web.appointmentmanage.dto.TicketDto;
|
||||
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 javax.annotation.Resource;
|
||||
@@ -198,10 +198,11 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
||||
if (SlotStatus.CHECKED_IN.equals(slotStatus)) {
|
||||
dto.setStatus("已取号");
|
||||
} else if (SlotStatus.BOOKED.equals(slotStatus)) {
|
||||
if (AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus())) {
|
||||
dto.setStatus("已取号");
|
||||
} else if (AppointmentOrderStatus.RETURNED.equals(raw.getOrderStatus())) {
|
||||
// order_main.status: 0=患者取消(已退号) 2=系统取消 其余=已预约
|
||||
if (OrderStatus.PATIENT_CANCELLED.getValue().equals(raw.getOrderStatus())) {
|
||||
dto.setStatus("已退号");
|
||||
} else if (OrderStatus.SYSTEM_CANCELLED.getValue().equals(raw.getOrderStatus())) {
|
||||
dto.setStatus("系统取消");
|
||||
} else {
|
||||
dto.setStatus("已预约");
|
||||
}
|
||||
@@ -372,10 +373,11 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
||||
if (SlotStatus.CHECKED_IN.equals(slotStatus)) {
|
||||
dto.setStatus("已取号");
|
||||
} else if (SlotStatus.BOOKED.equals(slotStatus)) {
|
||||
if (AppointmentOrderStatus.CHECKED_IN.equals(raw.getOrderStatus())) {
|
||||
dto.setStatus("已取号");
|
||||
} else if (AppointmentOrderStatus.RETURNED.equals(raw.getOrderStatus())) {
|
||||
// order_main.status: 0=患者取消(已退号) 2=系统取消 其余=已预约
|
||||
if (OrderStatus.PATIENT_CANCELLED.getValue().equals(raw.getOrderStatus())) {
|
||||
dto.setStatus("已退号");
|
||||
} else if (OrderStatus.SYSTEM_CANCELLED.getValue().equals(raw.getOrderStatus())) {
|
||||
dto.setStatus("系统取消");
|
||||
} else {
|
||||
dto.setStatus("已预约");
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.core.common.utils.MessageUtils;
|
||||
import com.openhis.administration.domain.Location;
|
||||
import com.openhis.administration.domain.Organization;
|
||||
import com.openhis.administration.domain.OrganizationLocation;
|
||||
import com.openhis.workflow.domain.ActivityDefinition;
|
||||
import com.openhis.administration.mapper.OrganizationLocationMapper;
|
||||
import com.openhis.administration.service.ILocationService;
|
||||
import com.openhis.administration.service.IOrganizationLocationService;
|
||||
@@ -70,6 +71,7 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation
|
||||
// 获取科室下拉选列表
|
||||
List<Organization> organizationList = organizationService.getList(OrganizationType.DEPARTMENT.getValue(), null);
|
||||
List<OrgLocInitDto.departmentOption> organizationOptions = organizationList.stream()
|
||||
.filter(organization -> organization != null && organization.getName() != null)
|
||||
.map(organization -> new OrgLocInitDto.departmentOption(organization.getId(), organization.getName()))
|
||||
.collect(Collectors.toList());
|
||||
initDto.setLocationFormOptions(chargeItemStatusOptions).setDepartmentOptions(organizationOptions);
|
||||
@@ -131,11 +133,18 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation
|
||||
@Override
|
||||
public R<?> addOrEditOrgLoc(OrgLocQueryDto orgLocQueryDto) {
|
||||
|
||||
// Validate required fields before processing
|
||||
if (orgLocQueryDto.getOrganizationId() == null) {
|
||||
return R.fail("请选择执行科室");
|
||||
}
|
||||
|
||||
OrganizationLocation orgLoc = new OrganizationLocation();
|
||||
BeanUtils.copyProperties(orgLocQueryDto, orgLoc);
|
||||
|
||||
Long activityDefinitionId = orgLoc.getActivityDefinitionId();
|
||||
String activityName = activityDefinitionId != null ? activityDefinitionMapper.selectById(activityDefinitionId).getName() : "";
|
||||
ActivityDefinition activityDef = activityDefinitionId != null
|
||||
? activityDefinitionMapper.selectById(activityDefinitionId) : null;
|
||||
String activityName = activityDef != null ? activityDef.getName() : "";
|
||||
|
||||
List<OrganizationLocation> organizationLocationList =
|
||||
organizationLocationService.getOrgLocListByOrgIdAndActivityDefinitionId(orgLoc.getActivityDefinitionId());
|
||||
@@ -147,8 +156,8 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation
|
||||
for (OrganizationLocation organizationLocation : organizationLocationList)
|
||||
if (DateTimeUtils.isOverlap(organizationLocation.getStartTime(), organizationLocation.getEndTime(),
|
||||
orgLoc.getStartTime(), orgLoc.getEndTime())) {
|
||||
String organizationName =
|
||||
organizationService.getById(organizationLocation.getOrganizationId()).getName();
|
||||
Organization org = organizationService.getById(organizationLocation.getOrganizationId());
|
||||
String organizationName = org != null ? org.getName() : "未知科室";
|
||||
return R.fail("当前诊疗:" + activityName + CommonConstants.Common.DASH + orgLoc.getStartTime()
|
||||
+ 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.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
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) {
|
||||
syncAppointmentReturnStatus(byId, cancelRegPaymentDto.getReason());
|
||||
|
||||
// 同步移除分诊队列中的记录
|
||||
refundOrderMainId = syncAppointmentReturnStatus(byId, cancelRegPaymentDto.getReason());
|
||||
removeTriageQueueItem(byId.getId());
|
||||
}
|
||||
|
||||
// 记录退号日志
|
||||
recordRefundLog(cancelRegPaymentDto, byId, result, paymentRecon);
|
||||
// 退号日志独立事务写入,无论退费成功与否均记录
|
||||
recordRefundLog(cancelRegPaymentDto, byId, result, paymentRecon, refundOrderMainId);
|
||||
|
||||
// 2025/05/05 该处保存费用项后,会通过统一收费处理进行收费
|
||||
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>()
|
||||
.eq(Order::getPatientId, encounter.getPatientId())
|
||||
.in(Order::getStatus, CommonConstants.AppointmentOrderStatus.BOOKED,
|
||||
CommonConstants.AppointmentOrderStatus.CHECKED_IN)
|
||||
.orderByDesc(Order::getUpdateTime)
|
||||
.orderByDesc(Order::getCreateTime)
|
||||
.last("LIMIT 1");
|
||||
@@ -590,20 +587,25 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步预约号源状态为已退号。
|
||||
* 说明:
|
||||
* 1) 门诊退号主流程不依赖该步骤成功与否,因此此方法内部异常仅记录日志,不向上抛出。
|
||||
* 2) 通过患者、科室、日期以及状态筛选最近一条预约订单,尽量避免误匹配。
|
||||
* 诊前退号:回滚预约订单、号源槽位、号源池统计。
|
||||
*
|
||||
* <p>处理四件事:
|
||||
* <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) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<Order>()
|
||||
.eq(Order::getPatientId, encounter.getPatientId())
|
||||
.in(Order::getStatus, CommonConstants.AppointmentOrderStatus.BOOKED,
|
||||
CommonConstants.AppointmentOrderStatus.CHECKED_IN)
|
||||
.orderByDesc(Order::getUpdateTime)
|
||||
.orderByDesc(Order::getCreateTime)
|
||||
.last("LIMIT 1");
|
||||
@@ -625,35 +627,55 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
||||
|
||||
Order appointmentOrder = orderService.getOne(queryWrapper, false);
|
||||
if (appointmentOrder == null) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
if (!CommonConstants.AppointmentOrderStatus.RETURNED.equals(appointmentOrder.getStatus())) {
|
||||
Order updateOrder = new Order();
|
||||
updateOrder.setId(appointmentOrder.getId());
|
||||
updateOrder.setStatus(CommonConstants.AppointmentOrderStatus.RETURNED);
|
||||
updateOrder.setCancelTime(now);
|
||||
updateOrder.setCancelReason(
|
||||
StringUtils.isNotEmpty(reason) ? reason : "门诊退号");
|
||||
updateOrder.setUpdateTime(now);
|
||||
orderService.updateById(updateOrder);
|
||||
// 只有有效订单(1)才能退号
|
||||
if (!OrderStatus.ACTIVE.getValue().equals(appointmentOrder.getStatus())) {
|
||||
log.warn("退号跳过:订单状态非有效, orderId={}, status={}",
|
||||
appointmentOrder.getId(), appointmentOrder.getStatus());
|
||||
return null;
|
||||
}
|
||||
|
||||
// 乐观锁更新:WHERE version = 旧值,防并发重复退号
|
||||
boolean updated = orderService.update(
|
||||
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();
|
||||
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) {
|
||||
Long poolId = scheduleSlotMapper.selectPoolIdBySlotId(slotId);
|
||||
if (poolId != null) {
|
||||
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) {
|
||||
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 encounter 就诊信息
|
||||
* @param result 退号结果
|
||||
* @param paymentRecon 支付对账信息
|
||||
* @param orderMainId 预约订单主键(order_main.id),用于关联业务数据
|
||||
*/
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void recordRefundLog(CancelRegPaymentDto cancelRegPaymentDto,
|
||||
Encounter encounter,
|
||||
R<?> result,
|
||||
PaymentReconciliation paymentRecon) {
|
||||
PaymentReconciliation paymentRecon,
|
||||
Long orderMainId) {
|
||||
RefundLog refundLog = new RefundLog();
|
||||
try {
|
||||
// 1. 订单ID(唯一)
|
||||
String orderId = String.valueOf(cancelRegPaymentDto.getEncounterId());
|
||||
// 1. 订单ID(关联 order_main.id)
|
||||
String orderId = orderMainId != null
|
||||
? String.valueOf(orderMainId)
|
||||
: String.valueOf(cancelRegPaymentDto.getEncounterId());
|
||||
refundLog.setOrderId(orderId);
|
||||
|
||||
// 已存在则不重复插入(防止唯一约束异常)
|
||||
|
||||
@@ -418,7 +418,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
||||
// 清除相关缓存
|
||||
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);
|
||||
|
||||
return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00002, new Object[]{"手术信息"}));
|
||||
return R.ok(null, "手术申请修改成功!");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,9 +2,13 @@ package com.openhis.web.clinicalmanage.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.openhis.common.enums.ActivityDefCategory;
|
||||
import com.openhis.web.clinicalmanage.appservice.ISurgicalScheduleAppService;
|
||||
import com.openhis.web.clinicalmanage.dto.OpCreateScheduleDto;
|
||||
import com.openhis.web.clinicalmanage.dto.OpScheduleDto;
|
||||
import com.openhis.web.regdoctorstation.appservice.IRequestFormManageAppService;
|
||||
import com.openhis.web.regdoctorstation.dto.RequestFormDto;
|
||||
import com.openhis.web.regdoctorstation.dto.RequestFormPageDto;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -26,6 +30,7 @@ import java.util.Map;
|
||||
public class SurgicalScheduleController {
|
||||
|
||||
private final ISurgicalScheduleAppService surgicalScheduleAppService;
|
||||
private final IRequestFormManageAppService requestFormManageAppService;
|
||||
|
||||
/**
|
||||
* 分页查询手术安排列表
|
||||
@@ -87,6 +92,27 @@ public class SurgicalScheduleController {
|
||||
return surgicalScheduleAppService.deleteSurgerySchedule(scheduleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询待排期手术申请列表
|
||||
*
|
||||
* @param requestFormDto 查询条件
|
||||
* @return 手术申请列表
|
||||
*/
|
||||
@PostMapping(value = "/apply-list")
|
||||
public R<IPage<RequestFormPageDto>> getSurgeryApplyList(@RequestBody RequestFormDto requestFormDto) {
|
||||
if (requestFormDto.getPageNo() == null) {
|
||||
requestFormDto.setPageNo(1);
|
||||
}
|
||||
if (requestFormDto.getPageSize() == null) {
|
||||
requestFormDto.setPageSize(10);
|
||||
}
|
||||
//虽然很想这么写,但是库里的手术申请单的type_code都是直接写的SURGERY
|
||||
// requestFormDto.setTypeCode(ActivityDefCategory.PROCEDURE.getCode());
|
||||
//只查询手术申请单
|
||||
requestFormDto.setTypeCode("SURGERY");
|
||||
return R.ok(requestFormManageAppService.getRequestFormPage(requestFormDto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出手术安排列表
|
||||
*
|
||||
|
||||
@@ -246,6 +246,16 @@ public class OpCreateScheduleDto {
|
||||
*/
|
||||
private String communicationInfo;
|
||||
|
||||
/**
|
||||
* 是否外请专家 1-是 0-否
|
||||
*/
|
||||
private Integer isExternalExpert;
|
||||
|
||||
/**
|
||||
* 外请专家姓名
|
||||
*/
|
||||
private String externalExpertName;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
|
||||
@@ -106,4 +106,9 @@ public class OpScheduleDto extends OpSchedule {
|
||||
* 创建人名称
|
||||
*/
|
||||
private String createByName;
|
||||
|
||||
/**
|
||||
* 费用类别
|
||||
*/
|
||||
private String feeType;
|
||||
}
|
||||
|
||||
@@ -59,6 +59,16 @@ public interface IDoctorStationAdviceAppService {
|
||||
*/
|
||||
R<?> getRequestBaseInfo(Long encounterId);
|
||||
|
||||
/**
|
||||
* 查询医嘱请求数据(支持按生成来源和来源单据号过滤)
|
||||
*
|
||||
* @param encounterId 就诊id
|
||||
* @param generateSourceEnum 生成来源(可选,如手术计费=6)
|
||||
* @param sourceBillNo 来源业务单据号(可选)
|
||||
* @return 医嘱请求数据
|
||||
*/
|
||||
R<?> getRequestBaseInfo(Long encounterId, Integer generateSourceEnum, String sourceBillNo);
|
||||
|
||||
/**
|
||||
* 门诊签退医嘱
|
||||
*
|
||||
|
||||
@@ -228,36 +228,58 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
// 医嘱定义ID集合
|
||||
List<Long> adviceDefinitionIdList = adviceBaseDtoList.stream().map(AdviceBaseDto::getAdviceDefinitionId)
|
||||
.collect(Collectors.toList());
|
||||
// 费用定价主表ID集合
|
||||
List<Long> chargeItemDefinitionIdList = adviceBaseDtoList.stream().map(AdviceBaseDto::getChargeItemDefinitionId)
|
||||
// 费用定价主表ID集合(过滤null值,手术项目无定价定义)
|
||||
List<Long> chargeItemDefinitionIdList = adviceBaseDtoList.stream()
|
||||
.map(AdviceBaseDto::getChargeItemDefinitionId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
// 医嘱库存集合
|
||||
List<AdviceInventoryDto> adviceInventoryList = doctorStationAdviceAppMapper.getAdviceInventory(locationId,
|
||||
adviceDefinitionIdList,
|
||||
CommonConstants.SqlCondition.ABOUT_INVENTORY_TABLE_STR, PublicationStatus.ACTIVE.getValue());
|
||||
// 待发放个数信息
|
||||
List<AdviceInventoryDto> adviceDraftInventoryList = doctorStationAdviceAppMapper.getAdviceDraftInventory(
|
||||
CommonConstants.TableName.MED_MEDICATION_DEFINITION, CommonConstants.TableName.ADM_DEVICE_DEFINITION,
|
||||
DispenseStatus.DRAFT.getValue(), DispenseStatus.PREPARATION.getValue());
|
||||
// 预减库存
|
||||
List<AdviceInventoryDto> adviceInventory = adviceUtils.subtractInventory(adviceInventoryList,
|
||||
adviceDraftInventoryList);
|
||||
// 查询取药科室配置
|
||||
List<AdviceInventoryDto> medLocationConfig = doctorStationAdviceAppMapper.getMedLocationConfig(organizationId);
|
||||
// 将配置转为 {categoryCode -> 允许的locationId集合}
|
||||
Map<String, Set<Long>> allowedLocByCategory = new HashMap<>();
|
||||
if (medLocationConfig != null && !medLocationConfig.isEmpty()) {
|
||||
for (AdviceInventoryDto cfg : medLocationConfig) {
|
||||
if (cfg.getCategoryCode() == null || cfg.getLocationId() == null) {
|
||||
continue;
|
||||
}
|
||||
allowedLocByCategory.computeIfAbsent(String.valueOf(cfg.getCategoryCode()), k -> new HashSet<>())
|
||||
.add(cfg.getLocationId());
|
||||
}
|
||||
|
||||
// 判断是否包含药品或耗材类型(只有这些类型才需要库存相关查询)
|
||||
boolean hasMedOrDevice = adviceTypes != null
|
||||
&& (adviceTypes.contains(1) || adviceTypes.contains(2));
|
||||
|
||||
// 医嘱库存集合 — 仅药品/耗材需要库存查询,手术/诊疗(3,6)无库存概念,跳过以减少数据库开销
|
||||
List<AdviceInventoryDto> adviceInventoryList;
|
||||
List<AdviceInventoryDto> adviceDraftInventoryList;
|
||||
List<AdviceInventoryDto> adviceInventory;
|
||||
if (hasMedOrDevice) {
|
||||
adviceInventoryList = doctorStationAdviceAppMapper.getAdviceInventory(locationId,
|
||||
adviceDefinitionIdList,
|
||||
CommonConstants.SqlCondition.ABOUT_INVENTORY_TABLE_STR, PublicationStatus.ACTIVE.getValue());
|
||||
// 待发放个数信息
|
||||
adviceDraftInventoryList = doctorStationAdviceAppMapper.getAdviceDraftInventory(
|
||||
CommonConstants.TableName.MED_MEDICATION_DEFINITION, CommonConstants.TableName.ADM_DEVICE_DEFINITION,
|
||||
DispenseStatus.DRAFT.getValue(), DispenseStatus.PREPARATION.getValue());
|
||||
// 预减库存
|
||||
adviceInventory = adviceUtils.subtractInventory(adviceInventoryList, adviceDraftInventoryList);
|
||||
} else {
|
||||
adviceInventoryList = Collections.emptyList();
|
||||
adviceDraftInventoryList = Collections.emptyList();
|
||||
adviceInventory = Collections.emptyList();
|
||||
}
|
||||
// 费用定价子表信息 - 使用分批处理避免大量参数问题
|
||||
// 查询取药科室配置 — 仅药品开单场景需要
|
||||
List<AdviceInventoryDto> medLocationConfig;
|
||||
Map<String, Set<Long>> allowedLocByCategory;
|
||||
if (hasMedOrDevice) {
|
||||
medLocationConfig = doctorStationAdviceAppMapper.getMedLocationConfig(organizationId);
|
||||
// 将配置转为 {categoryCode -> 允许的locationId集合}
|
||||
allowedLocByCategory = new HashMap<>();
|
||||
if (medLocationConfig != null && !medLocationConfig.isEmpty()) {
|
||||
for (AdviceInventoryDto cfg : medLocationConfig) {
|
||||
if (cfg.getCategoryCode() == null || cfg.getLocationId() == null) {
|
||||
continue;
|
||||
}
|
||||
allowedLocByCategory.computeIfAbsent(String.valueOf(cfg.getCategoryCode()), k -> new HashSet<>())
|
||||
.add(cfg.getLocationId());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
medLocationConfig = Collections.emptyList();
|
||||
allowedLocByCategory = Collections.emptyMap();
|
||||
}
|
||||
// 费用定价子表信息 - 仅药品/耗材需要批次定价查询,手术/诊疗无库存概念不需要
|
||||
List<AdvicePriceDto> childCharge = new ArrayList<>();
|
||||
if (chargeItemDefinitionIdList != null && !chargeItemDefinitionIdList.isEmpty()) {
|
||||
if (hasMedOrDevice && chargeItemDefinitionIdList != null && !chargeItemDefinitionIdList.isEmpty()) {
|
||||
// 分批处理,每批最多1000个ID,增加批次大小以减少查询次数
|
||||
int batchSize = 1000;
|
||||
for (int i = 0; i < chargeItemDefinitionIdList.size(); i += batchSize) {
|
||||
@@ -937,11 +959,16 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
}
|
||||
}
|
||||
for (AdviceSaveDto adviceSaveDto : deleteList) {
|
||||
iMedicationRequestService.removeById(adviceSaveDto.getRequestId());
|
||||
Long requestId = adviceSaveDto.getRequestId();
|
||||
// 🔧 Bug #442: 跳过 requestId 为 null 的记录,避免删除不存在的药品请求
|
||||
if (requestId == null) {
|
||||
log.warn("BugFix#442: handMedication - 跳过 requestId 为 null 的删除请求");
|
||||
continue;
|
||||
}
|
||||
iMedicationRequestService.removeById(requestId);
|
||||
// 删除已经产生的药品发放信息
|
||||
iMedicationDispenseService.deleteMedicationDispense(adviceSaveDto.getRequestId());
|
||||
// 🔧 Bug Fix #219: 删除费用项
|
||||
Long requestId = adviceSaveDto.getRequestId();
|
||||
String serviceTable = CommonConstants.TableName.MED_MEDICATION_REQUEST;
|
||||
// 直接删除费用项
|
||||
iChargeItemService.deleteByServiceTableAndId(serviceTable, requestId);
|
||||
@@ -1397,6 +1424,11 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
log.info("BugFix#219: handDevice - 开始删除循环, deleteList.size={}", deleteList.size());
|
||||
for (AdviceSaveDto adviceSaveDto : deleteList) {
|
||||
Long requestId = adviceSaveDto.getRequestId();
|
||||
// 🔧 Bug #442: 跳过 requestId 为 null 的记录,避免删除不存在的耗材请求
|
||||
if (requestId == null) {
|
||||
log.warn("BugFix#442: handDevice - 跳过 requestId 为 null 的删除请求");
|
||||
continue;
|
||||
}
|
||||
log.info("BugFix#219: handDevice - 删除开始: requestId={}", requestId);
|
||||
|
||||
// 1. 删除耗材请求
|
||||
@@ -1701,12 +1733,17 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
}
|
||||
}
|
||||
for (AdviceSaveDto adviceSaveDto : deleteList) {
|
||||
iServiceRequestService.removeById(adviceSaveDto.getRequestId());// 删除诊疗
|
||||
Long requestId = adviceSaveDto.getRequestId();
|
||||
// 🔧 Bug #442: 跳过 requestId 为 null 的记录,避免删除不存在的诊疗请求
|
||||
if (requestId == null) {
|
||||
log.warn("BugFix#442: handService - 跳过 requestId 为 null 的删除请求");
|
||||
continue;
|
||||
}
|
||||
iServiceRequestService.removeById(requestId);// 删除诊疗
|
||||
iServiceRequestService.remove(
|
||||
new LambdaQueryWrapper<ServiceRequest>().eq(ServiceRequest::getParentId,
|
||||
adviceSaveDto.getRequestId()));// 删除诊疗套餐对应的子项
|
||||
requestId));// 删除诊疗套餐对应的子项
|
||||
// 🔧 Bug Fix #219: 删除费用项
|
||||
Long requestId = adviceSaveDto.getRequestId();
|
||||
String serviceTable = CommonConstants.TableName.WOR_SERVICE_REQUEST;
|
||||
// 直接删除费用项
|
||||
iChargeItemService.deleteByServiceTableAndId(serviceTable, requestId);
|
||||
@@ -1763,8 +1800,9 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
log.info("handService - 自动补全founderOrgId: founderOrgId={}", adviceSaveDto.getFounderOrgId());
|
||||
}
|
||||
|
||||
// 🔧 Bug Fix #238: 诊疗项目执行科室非空校验
|
||||
if (adviceSaveDto.getAdviceType() != null && adviceSaveDto.getAdviceType() == 3) {
|
||||
// 🔧 Bug Fix #238/#454: 诊疗项目执行科室非空校验(删除操作跳过校验)
|
||||
if (adviceSaveDto.getAdviceType() != null && adviceSaveDto.getAdviceType() == 3
|
||||
&& !DbOpType.DELETE.getCode().equals(adviceSaveDto.getDbOpType())) {
|
||||
Long effectiveOrgId = adviceSaveDto.getEffectiveOrgId();
|
||||
if (effectiveOrgId == null) {
|
||||
throw new ServiceException("诊疗项目必须选择执行科室");
|
||||
@@ -1967,13 +2005,25 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
*/
|
||||
@Override
|
||||
public R<?> getRequestBaseInfo(Long encounterId) {
|
||||
return this.getRequestBaseInfo(encounterId, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public R<?> getRequestBaseInfo(Long encounterId, Integer generateSourceEnum, String sourceBillNo) {
|
||||
// 当前账号的参与者id
|
||||
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
|
||||
// 未指定generateSourceEnum时,默认只查询医生开立的医嘱
|
||||
int sourceEnum = (generateSourceEnum != null) ? generateSourceEnum : GenerateSource.DOCTOR_PRESCRIPTION.getValue();
|
||||
// 医嘱请求数据
|
||||
List<RequestBaseDto> requestBaseInfo = doctorStationAdviceAppMapper.getRequestBaseInfo(encounterId, null,
|
||||
CommonConstants.TableName.MED_MEDICATION_REQUEST, CommonConstants.TableName.WOR_DEVICE_REQUEST,
|
||||
CommonConstants.TableName.WOR_SERVICE_REQUEST, practitionerId, Whether.NO.getCode(),
|
||||
GenerateSource.DOCTOR_PRESCRIPTION.getValue());
|
||||
sourceEnum, sourceBillNo);
|
||||
// 手术计费场景:sourceBillNo 不为空时,只保留诊疗请求(3/6),过滤掉药品(1)和耗材(2)
|
||||
if (sourceBillNo != null && !sourceBillNo.isEmpty()) {
|
||||
requestBaseInfo.removeIf(dto -> dto.getAdviceType() != null
|
||||
&& (dto.getAdviceType() == 1 || dto.getAdviceType() == 2));
|
||||
}
|
||||
for (RequestBaseDto requestBaseDto : requestBaseInfo) {
|
||||
// 请求状态
|
||||
requestBaseDto
|
||||
@@ -2094,7 +2144,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
List<RequestBaseDto> requestBaseInfo = doctorStationAdviceAppMapper.getRequestBaseInfo(encounterId, patientId,
|
||||
CommonConstants.TableName.MED_MEDICATION_REQUEST, CommonConstants.TableName.WOR_DEVICE_REQUEST,
|
||||
CommonConstants.TableName.WOR_SERVICE_REQUEST, practitionerId, Whether.YES.getCode(),
|
||||
GenerateSource.DOCTOR_PRESCRIPTION.getValue());
|
||||
GenerateSource.DOCTOR_PRESCRIPTION.getValue(), null);
|
||||
for (RequestBaseDto requestBaseDto : requestBaseInfo) {
|
||||
// 请求状态
|
||||
requestBaseDto
|
||||
|
||||
@@ -274,27 +274,8 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
||||
return R.fail("非就诊中患者不能完诊");
|
||||
}
|
||||
|
||||
// 2. 获取 pool_id 和 slot_id:从 encounter → order_main → adm_schedule_slot 链路获取
|
||||
// 确保 div_log 中的值与排班主表一致,不依赖 triage_queue_item(队列项可能不存在或值错误)
|
||||
// 2. 查找队列项(限定当天,避免复诊患者匹配到历史队列记录)
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
Long divPoolId = null;
|
||||
Long divSlotId = null;
|
||||
if (encounter.getOrderId() != null) {
|
||||
try {
|
||||
Order order = iOrderService.getById(encounter.getOrderId());
|
||||
if (order != null && order.getSlotId() != null) {
|
||||
divSlotId = order.getSlotId();
|
||||
ScheduleSlot slot = scheduleSlotMapper.selectById(divSlotId);
|
||||
if (slot != null) {
|
||||
divPoolId = slot.getPoolId();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("获取完诊div_log的pool_id/slot_id失败,encounterId={}", encounterId, e);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 查找队列项(限定当天,避免复诊患者匹配到历史队列记录)
|
||||
TriageQueueItem queueItem = triageQueueItemService.getOne(
|
||||
new LambdaQueryWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
@@ -319,14 +300,43 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 获取 pool_id 和 slot_id:优先使用 triage_queue_item(挂号时录入的号源信息,为权威来源)
|
||||
// 队列项不存在或值缺失时,回退使用 encounter → order_main → adm_schedule_slot 链路
|
||||
Long divPoolId = null;
|
||||
Long divSlotId = null;
|
||||
if (queueItem != null && queueItem.getPoolId() != null && queueItem.getSlotId() != null) {
|
||||
divPoolId = queueItem.getPoolId();
|
||||
divSlotId = queueItem.getSlotId();
|
||||
}
|
||||
// 队列项 poolId/slotId 缺失时,通过 encounter.orderId → order_main.slot_id → adm_schedule_slot.pool_id 回退获取
|
||||
if ((divPoolId == null || divSlotId == null) && encounter.getOrderId() != null) {
|
||||
try {
|
||||
Order order = iOrderService.getById(encounter.getOrderId());
|
||||
if (order != null && order.getSlotId() != null) {
|
||||
ScheduleSlot slot = scheduleSlotMapper.selectById(order.getSlotId());
|
||||
if (slot != null) {
|
||||
divSlotId = slot.getId();
|
||||
divPoolId = slot.getPoolId();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("回退获取完诊div_log的pool_id/slot_id失败,encounterId={}", encounterId, e);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果队列项存在且未完成,更新队列状态为已完成
|
||||
// 使用排除法而非白名单:只要不是"已完成"就可以完诊,覆盖跳过、等待等非标准流转状态
|
||||
if (queueItem != null &&
|
||||
!TriageQueueStatus.COMPLETED.getValue().equals(queueItem.getStatus())) {
|
||||
java.time.LocalDateTime nowLocal = java.time.LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS);
|
||||
queueItem.setStatus(TriageQueueStatus.COMPLETED.getValue());
|
||||
queueItem.setUpdateTime(nowLocal);
|
||||
triageQueueItemService.updateById(queueItem);
|
||||
// 使用 LambdaUpdateWrapper 直接更新,确保 status 字段必定写入数据库
|
||||
boolean queueUpdate = triageQueueItemService.update(new LambdaUpdateWrapper<TriageQueueItem>()
|
||||
.eq(TriageQueueItem::getId, queueItem.getId())
|
||||
.set(TriageQueueItem::getStatus, TriageQueueStatus.COMPLETED.getValue())
|
||||
.set(TriageQueueItem::getUpdateTime, nowLocal));
|
||||
if (!queueUpdate) {
|
||||
log.error("完诊:triage_queue_item 状态更新失败,queueItemId={}", queueItem.getId());
|
||||
}
|
||||
} else if (queueItem == null) {
|
||||
log.error("完诊:未找到任何 triage_queue_item 记录,encounterId={}, tenantId={}",
|
||||
encounterId, tenantId);
|
||||
|
||||
@@ -112,11 +112,16 @@ public class DoctorStationAdviceController {
|
||||
* 查询医嘱请求数据
|
||||
*
|
||||
* @param encounterId 就诊id
|
||||
* @param generateSourceEnum 生成来源(可选,用于按来源过滤,如手术计费=6)
|
||||
* @param sourceBillNo 来源业务单据号(可选,用于按来源单据过滤)
|
||||
* @return 医嘱请求数据
|
||||
*/
|
||||
@GetMapping(value = "/request-base-info")
|
||||
public R<?> getRequestBaseInfo(@RequestParam(required = false) Long encounterId) {
|
||||
return iDoctorStationAdviceAppService.getRequestBaseInfo(encounterId);
|
||||
public R<?> getRequestBaseInfo(
|
||||
@RequestParam(required = false) Long encounterId,
|
||||
@RequestParam(required = false) Integer generateSourceEnum,
|
||||
@RequestParam(required = false) String sourceBillNo) {
|
||||
return iDoctorStationAdviceAppService.getRequestBaseInfo(encounterId, generateSourceEnum, sourceBillNo);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.openhis.web.doctorstation.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import lombok.Data;
|
||||
@@ -55,6 +56,7 @@ public class InfectiousDiseaseReportDto {
|
||||
private String sex;
|
||||
|
||||
/** 出生日期 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date birthday;
|
||||
|
||||
/** 实足年龄 */
|
||||
|
||||
@@ -122,7 +122,8 @@ public interface DoctorStationAdviceAppMapper {
|
||||
@Param("MED_MEDICATION_REQUEST") String MED_MEDICATION_REQUEST,
|
||||
@Param("WOR_DEVICE_REQUEST") String WOR_DEVICE_REQUEST,
|
||||
@Param("WOR_SERVICE_REQUEST") String WOR_SERVICE_REQUEST, @Param("practitionerId") Long practitionerId,
|
||||
@Param("historyFlag") String historyFlag, @Param("generateSourceEnum") Integer generateSourceEnum);
|
||||
@Param("historyFlag") String historyFlag, @Param("generateSourceEnum") Integer generateSourceEnum,
|
||||
@Param("sourceBillNo") String sourceBillNo);
|
||||
|
||||
/**
|
||||
* 查询就诊费用性质
|
||||
|
||||
@@ -83,9 +83,14 @@ public class AdviceUtils {
|
||||
* @return 提示信息
|
||||
*/
|
||||
public String checkInventory(List<AdviceSaveDto> adviceSaveList) {
|
||||
// 医嘱定义ID集合
|
||||
// 医嘱定义ID集合(过滤null值,避免SQL查询异常)
|
||||
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
|
||||
= doctorStationAdviceAppMapper.getAdviceInventory(null, adviceDefinitionIdList,
|
||||
@@ -99,6 +104,10 @@ public class AdviceUtils {
|
||||
= this.subtractInventory(adviceInventoryList, adviceDraftInventoryList);
|
||||
// 检查库存
|
||||
for (AdviceSaveDto saveDto : adviceSaveList) {
|
||||
// 🔧 Bug #504 修复:退回医嘱可能adviceDefinitionId为空,跳过校验
|
||||
if (saveDto.getAdviceDefinitionId() == null) {
|
||||
continue;
|
||||
}
|
||||
boolean matched = false;
|
||||
for (AdviceInventoryDto inventoryDto : adviceInventory) {
|
||||
// 匹配条件:adviceDefinitionId, adviceTableName, locationId, lotNumber 同时相等
|
||||
@@ -108,10 +117,12 @@ public class AdviceUtils {
|
||||
|| saveDto.getLotNumber().equals(inventoryDto.getLotNumber());
|
||||
boolean tableNameMatch = StringUtils.isEmpty(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())
|
||||
&& tableNameMatch
|
||||
&& inventoryDto.getLocationId().equals(saveDto.getLocationId()) && lotNumberMatch) {
|
||||
&& locationMatch && lotNumberMatch) {
|
||||
matched = true;
|
||||
// 检查库存是否充足
|
||||
BigDecimal minUnitQuantity = saveDto.getMinUnitQuantity();
|
||||
@@ -167,22 +178,26 @@ public class AdviceUtils {
|
||||
// 生命提示信息集合
|
||||
List<String> tipsList = new ArrayList<>();
|
||||
for (MedicationRequestUseExe medicationRequestUseExe : medUseExeList) {
|
||||
Optional<AdviceInventoryDto> matchedInventory = adviceInventory.stream()
|
||||
// 聚合同一位置所有批次的库存总量
|
||||
List<AdviceInventoryDto> matchedInventories = adviceInventory.stream()
|
||||
.filter(inventoryDto -> medicationRequestUseExe.getMedicationId().equals(inventoryDto.getItemId())
|
||||
&& CommonConstants.TableName.MED_MEDICATION_DEFINITION.equals(inventoryDto.getItemTable())
|
||||
&& medicationRequestUseExe.getPerformLocation().equals(inventoryDto.getLocationId())
|
||||
// 如果选择了具体的批次号,校验库存时需要加上批次号的匹配条件
|
||||
&& (StringUtils.isEmpty(medicationRequestUseExe.getLotNumber())
|
||||
|| medicationRequestUseExe.getLotNumber().equals(inventoryDto.getLotNumber())))
|
||||
.findFirst();
|
||||
.collect(Collectors.toList());
|
||||
// 匹配到库存信息
|
||||
if (matchedInventory.isPresent()) {
|
||||
AdviceInventoryDto inventoryDto = matchedInventory.get();
|
||||
if ((medicationRequestUseExe.getExecuteTimesNum()
|
||||
.multiply(medicationRequestUseExe.getMinUnitQuantity()))
|
||||
.compareTo(inventoryDto.getQuantity()) > 0) {
|
||||
if (!matchedInventories.isEmpty()) {
|
||||
// 聚合所有批次的可用库存
|
||||
BigDecimal totalQuantity = matchedInventories.stream()
|
||||
.map(dto -> dto.getQuantity() != null ? dto.getQuantity() : BigDecimal.ZERO)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
BigDecimal requestQuantity = medicationRequestUseExe.getExecuteTimesNum()
|
||||
.multiply(medicationRequestUseExe.getMinUnitQuantity());
|
||||
if (requestQuantity.compareTo(totalQuantity) > 0) {
|
||||
tipsList
|
||||
.add("【" + medicationRequestUseExe.getBusNo() + "】在" + inventoryDto.getLocationName() + "库存不足");
|
||||
.add("【" + medicationRequestUseExe.getBusNo() + "】在" + matchedInventories.get(0).getLocationName() + "库存不足");
|
||||
}
|
||||
} else {
|
||||
tipsList.add("【" + medicationRequestUseExe.getBusNo() + "】未匹配到库存信息");
|
||||
|
||||
@@ -206,6 +206,8 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
||||
e.setTherapyEnum_enumText(EnumUtils.getInfoByValue(TherapyTimeType.class, e.getTherapyEnum()));
|
||||
// 请求状态
|
||||
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()));
|
||||
// 计算年龄
|
||||
@@ -356,6 +358,17 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
||||
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();
|
||||
Date checkDate = new Date();
|
||||
if (!serviceRequestList.isEmpty()) {
|
||||
|
||||
@@ -255,4 +255,8 @@ public class InpatientAdviceDto {
|
||||
|
||||
/** 总价 */
|
||||
private BigDecimal totalPrice;
|
||||
|
||||
/** 发药状态 */
|
||||
private Integer dispenseStatus;
|
||||
private String dispenseStatus_enumText;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ import com.openhis.common.constant.PromptMsgConstant;
|
||||
import com.openhis.common.enums.*;
|
||||
import com.openhis.common.utils.EnumUtils;
|
||||
import com.openhis.common.utils.HisQueryUtils;
|
||||
import com.core.common.utils.SecurityUtils;
|
||||
import com.openhis.workflow.domain.InventoryItem;
|
||||
import com.openhis.workflow.service.IInventoryItemService;
|
||||
import com.openhis.web.basedatamanage.dto.LocationDto;
|
||||
import com.openhis.web.common.dto.UnitDto;
|
||||
import com.openhis.web.inventorymanage.appservice.IProductTransferAppService;
|
||||
@@ -57,6 +60,9 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService
|
||||
@Autowired
|
||||
private IPractitionerService practitionerService;
|
||||
|
||||
@Autowired
|
||||
private IInventoryItemService inventoryItemService;
|
||||
|
||||
/**
|
||||
* 商品调拨页面初始化
|
||||
*
|
||||
@@ -196,6 +202,23 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService
|
||||
@Override
|
||||
public R<?> addOrEditBatchTransferReceipt(List<ProductTransferDto> productTransferDtoList, Boolean flag) {
|
||||
|
||||
// 校验调拨数量:必须 > 0 且不超过源库存数量(从数据库查实时库存)
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
for (ProductTransferDto dto : productTransferDtoList) {
|
||||
if (dto.getItemQuantity() == null || dto.getItemQuantity().compareTo(java.math.BigDecimal.ZERO) <= 0) {
|
||||
return R.fail("调拨数量必须大于0");
|
||||
}
|
||||
// 查询该药品在源仓库的实时库存总量
|
||||
List<InventoryItem> inventoryList = inventoryItemService.selectInventoryByItemId(
|
||||
dto.getItemId(), dto.getLotNumber(), dto.getSourceLocationId(), tenantId);
|
||||
java.math.BigDecimal actualStock = inventoryList.stream()
|
||||
.map(InventoryItem::getQuantity)
|
||||
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
|
||||
if (dto.getItemQuantity().compareTo(actualStock) > 0) {
|
||||
return R.fail("调拨数量不可超出源库存数量(当前库存:" + actualStock + ")");
|
||||
}
|
||||
}
|
||||
|
||||
List<String> idList = new ArrayList<>();
|
||||
if (flag) {
|
||||
// 批量保存按钮
|
||||
@@ -309,6 +332,22 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService
|
||||
@Override
|
||||
public R<?> addOrEditTransferReceipt(List<ProductTransferDto> productTransferDtoList) {
|
||||
|
||||
// 校验调拨数量:必须 > 0 且不超过源库存数量(从数据库查实时库存)
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
for (ProductTransferDto dto : productTransferDtoList) {
|
||||
if (dto.getItemQuantity() == null || dto.getItemQuantity().compareTo(java.math.BigDecimal.ZERO) <= 0) {
|
||||
return R.fail("调拨数量必须大于0");
|
||||
}
|
||||
List<InventoryItem> inventoryList = inventoryItemService.selectInventoryByItemId(
|
||||
dto.getItemId(), dto.getLotNumber(), dto.getSourceLocationId(), tenantId);
|
||||
java.math.BigDecimal actualStock = inventoryList.stream()
|
||||
.map(InventoryItem::getQuantity)
|
||||
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
|
||||
if (dto.getItemQuantity().compareTo(actualStock) > 0) {
|
||||
return R.fail("调拨数量不可超出源库存数量(当前库存:" + actualStock + ")");
|
||||
}
|
||||
}
|
||||
|
||||
List<String> idList = new ArrayList<>();
|
||||
|
||||
// 单据号取得
|
||||
@@ -380,6 +419,25 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService
|
||||
*/
|
||||
@Override
|
||||
public R<?> submitApproval(String busNo) {
|
||||
// 提交前再次校验调拨数量(从数据库查实时库存)
|
||||
List<SupplyRequest> requestList = supplyRequestService.getSupplyByBusNo(busNo);
|
||||
if (requestList != null && !requestList.isEmpty()) {
|
||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||
for (SupplyRequest request : requestList) {
|
||||
if (request.getItemQuantity() == null || request.getItemQuantity().compareTo(java.math.BigDecimal.ZERO) <= 0) {
|
||||
return R.fail("调拨数量必须大于0,请检查后重新保存");
|
||||
}
|
||||
// 查询该药品在源仓库的实时库存总量
|
||||
List<InventoryItem> inventoryList = inventoryItemService.selectInventoryByItemId(
|
||||
request.getItemId(), request.getLotNumber(), request.getSourceLocationId(), tenantId);
|
||||
java.math.BigDecimal actualStock = inventoryList.stream()
|
||||
.map(InventoryItem::getQuantity)
|
||||
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
|
||||
if (request.getItemQuantity().compareTo(actualStock) > 0) {
|
||||
return R.fail("调拨数量不可超出源库存数量(当前库存:" + actualStock + "),请检查后重新保存");
|
||||
}
|
||||
}
|
||||
}
|
||||
// 单据提交审核
|
||||
boolean result = supplyRequestService.submitApproval(busNo);
|
||||
return result ? R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, null))
|
||||
|
||||
@@ -1991,7 +1991,7 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
|
||||
Order appointmentOrder = iOrderService.getOne(
|
||||
new LambdaQueryWrapper<Order>()
|
||||
.eq(Order::getPatientId, encounterFormData.getPatientId())
|
||||
.eq(Order::getStatus, CommonConstants.AppointmentOrderStatus.CHECKED_IN)
|
||||
.eq(Order::getStatus, OrderStatus.ACTIVE.getValue()) // 有效订单(1)
|
||||
.eq(Order::getDeleteFlag, "0")
|
||||
.orderByDesc(Order::getCreateTime)
|
||||
.last("LIMIT 1")
|
||||
@@ -2114,11 +2114,11 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
|
||||
Long queuePoolId = null;
|
||||
Long queueSlotId = null;
|
||||
try {
|
||||
// 查询患者当天的待签到预约订单(status = 1 或 2 表示已预约或已取号)
|
||||
// 查询患者当天有效订单(1);已取消(0/2)和已完成(3)的不参与排队
|
||||
Order order = iOrderService.getOne(
|
||||
new LambdaQueryWrapper<Order>()
|
||||
.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")
|
||||
.orderByDesc(Order::getCreateTime)
|
||||
.last("LIMIT 1")
|
||||
|
||||
@@ -39,10 +39,22 @@ public interface IRequestFormManageAppService {
|
||||
* @param typeCode 申请单类型
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @param status 单据状态(可选)
|
||||
* @return 申请单列表
|
||||
*/
|
||||
List<RequestFormQueryDto> getRequestForm(Long encounterId, String typeCode, String startDate, String endDate, String status);
|
||||
List<RequestFormQueryDto> getRequestForm(Long encounterId, String typeCode, String startDate, String endDate);
|
||||
|
||||
/**
|
||||
* 查询申请单(支持筛选+状态+关键字)
|
||||
*
|
||||
* @param encounterId 就诊id
|
||||
* @param typeCode 申请单类型
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @param status 单据状态(可选)
|
||||
* @param keyword 关键字(可选,申请单号/项目名称模糊匹配)
|
||||
* @return 申请单列表
|
||||
*/
|
||||
List<RequestFormQueryDto> getRequestForm(Long encounterId, String typeCode, String startDate, String endDate, String status, String keyword);
|
||||
|
||||
/**
|
||||
* 分页查询申请单
|
||||
|
||||
@@ -341,6 +341,28 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
&& (DbOpType.INSERT.getCode().equals(e.getDbOpType()) || DbOpType.UPDATE.getCode().equals(e.getDbOpType())))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 防重复保存:对新增医嘱进行去重,避免签发单条长期医嘱时产生重复记录
|
||||
Set<String> longUniqueKeySet = new HashSet<>();
|
||||
List<RegAdviceSaveDto> longUniqueList = new ArrayList<>();
|
||||
for (RegAdviceSaveDto adviceSaveDto : longInsertOrUpdateList) {
|
||||
String uniqueKey = adviceSaveDto.getPatientId() + "_"
|
||||
+ adviceSaveDto.getEncounterId() + "_"
|
||||
+ adviceSaveDto.getAdviceDefinitionId() + "_"
|
||||
+ adviceSaveDto.getDose() + "_"
|
||||
+ adviceSaveDto.getMethodCode() + "_"
|
||||
+ adviceSaveDto.getRateCode();
|
||||
if (DbOpType.INSERT.getCode().equals(adviceSaveDto.getDbOpType()) && longUniqueKeySet.contains(uniqueKey)) {
|
||||
log.warn("防重复保存:检测到重复长期医嘱,跳过保存 - patientId={}, encounterId={}, adviceDefinitionId={}, dose={}",
|
||||
adviceSaveDto.getPatientId(), adviceSaveDto.getEncounterId(),
|
||||
adviceSaveDto.getAdviceDefinitionId(), adviceSaveDto.getDose());
|
||||
continue;
|
||||
}
|
||||
longUniqueKeySet.add(uniqueKey);
|
||||
longUniqueList.add(adviceSaveDto);
|
||||
}
|
||||
log.info("防重复保存(长期):去重前{}条,去重后{}条", longInsertOrUpdateList.size(), longUniqueList.size());
|
||||
longInsertOrUpdateList = longUniqueList;
|
||||
|
||||
for (RegAdviceSaveDto regAdviceSaveDto : longInsertOrUpdateList) {
|
||||
boolean firstTimeSave = false;// 第一次保存
|
||||
longMedicationRequest = new MedicationRequest();
|
||||
@@ -406,6 +428,29 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
.getValue().equals(e.getTherapyEnum())
|
||||
&& (DbOpType.INSERT.getCode().equals(e.getDbOpType()) || DbOpType.UPDATE.getCode().equals(e.getDbOpType())))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 防重复保存:对新增医嘱进行去重
|
||||
Set<String> tempUniqueKeySet = new HashSet<>();
|
||||
List<RegAdviceSaveDto> tempUniqueList = new ArrayList<>();
|
||||
for (RegAdviceSaveDto adviceSaveDto : tempInsertOrUpdateList) {
|
||||
String uniqueKey = adviceSaveDto.getPatientId() + "_"
|
||||
+ adviceSaveDto.getEncounterId() + "_"
|
||||
+ adviceSaveDto.getAdviceDefinitionId() + "_"
|
||||
+ adviceSaveDto.getDose() + "_"
|
||||
+ adviceSaveDto.getMethodCode() + "_"
|
||||
+ adviceSaveDto.getRateCode();
|
||||
if (DbOpType.INSERT.getCode().equals(adviceSaveDto.getDbOpType()) && tempUniqueKeySet.contains(uniqueKey)) {
|
||||
log.warn("防重复保存:检测到重复临时医嘱,跳过保存 - patientId={}, encounterId={}, adviceDefinitionId={}, dose={}",
|
||||
adviceSaveDto.getPatientId(), adviceSaveDto.getEncounterId(),
|
||||
adviceSaveDto.getAdviceDefinitionId(), adviceSaveDto.getDose());
|
||||
continue;
|
||||
}
|
||||
tempUniqueKeySet.add(uniqueKey);
|
||||
tempUniqueList.add(adviceSaveDto);
|
||||
}
|
||||
log.info("防重复保存(临时):去重前{}条,去重后{}条", tempInsertOrUpdateList.size(), tempUniqueList.size());
|
||||
tempInsertOrUpdateList = tempUniqueList;
|
||||
|
||||
for (RegAdviceSaveDto regAdviceSaveDto : tempInsertOrUpdateList) {
|
||||
boolean firstTimeSave = false;// 第一次保存
|
||||
tempMedicationRequest = new MedicationRequest();
|
||||
@@ -511,6 +556,9 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
// 签发操作
|
||||
boolean is_sign = AdviceOpType.SIGN_ADVICE.getCode().equals(adviceOpType);
|
||||
|
||||
// 收集已处理的requestId,用于批量更新状态
|
||||
List<Long> processedRequestIds = new ArrayList<>();
|
||||
|
||||
// 声明长期医嘱诊疗请求
|
||||
ServiceRequest longServiceRequest;
|
||||
// 新增 + 修改 (长期医嘱)
|
||||
@@ -555,6 +603,9 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
}
|
||||
}
|
||||
iServiceRequestService.saveOrUpdate(longServiceRequest);
|
||||
if (longServiceRequest.getId() != null) {
|
||||
processedRequestIds.add(longServiceRequest.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// 声明临时医嘱诊疗请求
|
||||
@@ -603,6 +654,9 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
}
|
||||
}
|
||||
iServiceRequestService.saveOrUpdate(tempServiceRequest);
|
||||
if (tempServiceRequest.getId() != null) {
|
||||
processedRequestIds.add(tempServiceRequest.getId());
|
||||
}
|
||||
|
||||
// 保存时,保存诊疗费用项
|
||||
if (is_save) {
|
||||
@@ -654,6 +708,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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -75,6 +76,13 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> saveRequestForm(RequestFormSaveDto requestFormSaveDto, String typeCode) {
|
||||
// 诊疗执行科室配置校验(必须在任何数据库操作之前)
|
||||
List<ActivityOrganizationConfigDto> activityOrganizationConfig =
|
||||
requestFormManageAppMapper.getActivityOrganizationConfig(typeCode);
|
||||
if (activityOrganizationConfig.isEmpty()) {
|
||||
throw new ServiceException("请先配置当前时间段的执行科室");
|
||||
}
|
||||
|
||||
// 诊疗处方号
|
||||
String prescriptionNo;
|
||||
// 申请单ID
|
||||
@@ -91,8 +99,10 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
return R.fail("无待签发的医嘱,该申请单不可编辑");
|
||||
}
|
||||
} else {
|
||||
// 诊疗处方号
|
||||
prescriptionNo = assignSeqUtil.getSeq(AssignSeqEnum.ACTIVITY_PSYCHOTROPIC_NO.getPrefix(), 8);
|
||||
// 检查申请单号:JC(检查)+ Z(住院标识)+ yyMMdd(日期)+ 5位顺序号
|
||||
String dateStr = new java.text.SimpleDateFormat("yyMMdd").format(new Date());
|
||||
int seq = assignSeqUtil.getSeqNoByDay(AssignSeqEnum.CHECK_APPLY_NO.getPrefix());
|
||||
prescriptionNo = "JCZ" + dateStr + String.format("%05d", seq);
|
||||
}
|
||||
|
||||
// 当前时间
|
||||
@@ -139,12 +149,6 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
// 诊疗集合
|
||||
List<ActivitySaveDto> activityList = requestFormSaveDto.getActivityList();
|
||||
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) {
|
||||
serviceRequest = new ServiceRequest();
|
||||
@@ -414,7 +418,7 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
@Override
|
||||
public List<RequestFormQueryDto> getRequestForm(Long encounterId, String typeCode) {
|
||||
// 调用重载方法,不传筛选参数
|
||||
return getRequestForm(encounterId, typeCode, null, null, null);
|
||||
return getRequestForm(encounterId, typeCode, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -424,17 +428,32 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
* @param typeCode 申请单类型
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @param status 单据状态(可选)
|
||||
* @return 申请单列表
|
||||
*/
|
||||
@Override
|
||||
public List<RequestFormQueryDto> getRequestForm(Long encounterId, String typeCode, String startDate, String endDate, String status) {
|
||||
public List<RequestFormQueryDto> getRequestForm(Long encounterId, String typeCode, String startDate, String endDate) {
|
||||
return getRequestForm(encounterId, typeCode, startDate, endDate, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询申请单(支持筛选+状态+关键字)
|
||||
*
|
||||
* @param encounterId 就诊id
|
||||
* @param typeCode 申请单类型
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @param status 单据状态(可选)
|
||||
* @param keyword 关键字(可选,申请单号/项目名称模糊匹配)
|
||||
* @return 申请单列表
|
||||
*/
|
||||
@Override
|
||||
public List<RequestFormQueryDto> getRequestForm(Long encounterId, String typeCode, String startDate, String endDate, String status, String keyword) {
|
||||
// 检查参数
|
||||
if (encounterId == null) {
|
||||
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, keyword);
|
||||
for (RequestFormQueryDto requestFormQueryDto : requestFormList) {
|
||||
// 查询处方详情
|
||||
List<RequestFormDetailQueryDto> requestFormDetail =
|
||||
|
||||
@@ -16,6 +16,7 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -98,6 +99,7 @@ public class RequestFormManageController {
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @param status 单据状态(可选)
|
||||
* @param keyword 关键字(可选,申请单号/检验项目模糊匹配)
|
||||
* @return 检验申请单
|
||||
*/
|
||||
@GetMapping(value = "/get-inspection")
|
||||
@@ -105,11 +107,12 @@ public class RequestFormManageController {
|
||||
@RequestParam(required = false) Long encounterId,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate,
|
||||
@RequestParam(required = false) String status) {
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
if (encounterId == null) {
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,6 +143,32 @@ public class RequestFormManageController {
|
||||
return R.ok(iRequestFormManageAppService.getRequestForm(encounterId, ActivityDefCategory.PROCEDURE.getCode()));
|
||||
}
|
||||
/**
|
||||
* 分页查询手术申请单(全局,不需要encounterId,用于门诊手术安排查找弹窗)
|
||||
*
|
||||
* @param surgeryNo 手术单号(模糊查询,可选)
|
||||
* @param applyTimeStart 申请时间起始(可选)
|
||||
* @param applyTimeEnd 申请时间截止(可选)
|
||||
* @param mainDoctorId 主刀医生ID(可选)
|
||||
* @param applyDeptId 申请科室ID(可选)
|
||||
* @param pageNo 页码,默认1
|
||||
* @param pageSize 每页数量,默认10
|
||||
* @return 分页手术申请单列表
|
||||
*/
|
||||
@GetMapping(value = "/get-surgery-page")
|
||||
public R<IPage<RequestFormPageDto>> getSurgeryRequestFormPage(
|
||||
@RequestParam(required = false) String surgeryNo,
|
||||
@RequestParam(required = false) LocalDate applyTimeStart,
|
||||
@RequestParam(required = false) LocalDate applyTimeEnd,
|
||||
@RequestParam(required = false) Long mainDoctorId,
|
||||
@RequestParam(required = false) Long applyDeptId,
|
||||
@RequestParam(defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
RequestFormDto dto = new RequestFormDto(surgeryNo, ActivityDefCategory.PROCEDURE.getCode(), applyTimeStart, applyTimeEnd,
|
||||
mainDoctorId, applyDeptId, pageNo, pageSize);
|
||||
return R.ok(iRequestFormManageAppService.getRequestFormPage(dto));
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询申请单
|
||||
* @return 申请单
|
||||
*/
|
||||
|
||||
@@ -14,6 +14,10 @@ public class RequestFormDto {
|
||||
* 手术单号
|
||||
*/
|
||||
private String surgeryNo;
|
||||
/**
|
||||
* 申请单类型编码
|
||||
*/
|
||||
private String typeCode;
|
||||
/**
|
||||
* 申请时间开始
|
||||
*/
|
||||
|
||||
@@ -37,13 +37,15 @@ public interface RequestFormManageAppMapper {
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @param status 单据状态(可选)
|
||||
* @param keyword 关键字(可选,申请单号/检查项目名称模糊匹配)
|
||||
* @return 申请单列表
|
||||
*/
|
||||
List<RequestFormQueryDto> getRequestForm(@Param("encounterId") Long encounterId,
|
||||
@Param("typeCode") String typeCode,
|
||||
@Param("startDate") String startDate,
|
||||
@Param("endDate") String endDate,
|
||||
@Param("status") String status);
|
||||
@Param("status") String status,
|
||||
@Param("keyword") String keyword);
|
||||
|
||||
/**
|
||||
* 查询申请单详情
|
||||
|
||||
@@ -6,7 +6,7 @@ spring:
|
||||
druid:
|
||||
# 主库数据源
|
||||
master:
|
||||
url: jdbc:postgresql://192.168.110.252:15432/postgresql?currentSchema=histest&characterEncoding=UTF-8&client_encoding=UTF-8
|
||||
url: jdbc:postgresql://192.168.110.252:15432/postgresql?currentSchema=histest1&characterEncoding=UTF-8&client_encoding=UTF-8
|
||||
username: postgresql
|
||||
password: Jchl1528
|
||||
# 从库数据源
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
WHEN T1.context_enum = 6 AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN COALESCE(wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = 6 THEN T2."name"
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN COALESCE(wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = #{activity} AND T1.service_table = 'wor_service_request' THEN COALESCE(T9.surgery_name, wsr.content_json::json->>'surgeryName', wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = #{activity} THEN COALESCE(wsr.content_json::json->>'surgeryName', wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = #{medication} THEN T3."name"
|
||||
WHEN T1.context_enum = #{device} THEN T4."name"
|
||||
@@ -225,6 +226,7 @@
|
||||
WHEN T1.context_enum = 6 AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN COALESCE(wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = 6 THEN T2."name"
|
||||
WHEN T1.context_enum = #{activity} AND T1.product_id = 0 AND T1.service_table = 'wor_service_request' THEN COALESCE(wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = #{activity} AND T1.service_table = 'wor_service_request' THEN COALESCE(T9.surgery_name, wsr.content_json::json->>'surgeryName', wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = #{activity} THEN COALESCE(wsr.content_json::json->>'surgeryName', wsr.content_json::json->>'adviceName', T2."name")
|
||||
WHEN T1.context_enum = #{medication} THEN T3."name"
|
||||
WHEN T1.context_enum = #{device} THEN T4."name"
|
||||
|
||||
@@ -331,12 +331,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
) t
|
||||
WHERE rn = 1
|
||||
) 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
|
||||
<where>
|
||||
s.delete_flag = '0'
|
||||
<!-- 只显示未生成医嘱的手术 -->
|
||||
AND sr.id IS NULL
|
||||
<if test="ew.sqlSegment != null and ew.sqlSegment != ''">
|
||||
<!-- 补充 encounter_id 替换,修复多表关联时字段歧义。注释不能放进 OGNL 表达式内部。 -->
|
||||
<![CDATA[
|
||||
|
||||
@@ -89,11 +89,15 @@
|
||||
cs.apply_doctor_name AS apply_doctor_name,
|
||||
drf.create_time AS apply_time,
|
||||
os.surgery_nature AS surgeryType,
|
||||
fc.contract_name AS feeType,
|
||||
COALESCE(pi.identifier_no, ap.bus_no, '') AS identifierNo
|
||||
FROM op_schedule os
|
||||
LEFT JOIN adm_patient ap ON os.patient_id = ap.id
|
||||
INNER JOIN cli_surgery cs ON os.oper_code = cs.surgery_no AND cs.delete_flag = '0'
|
||||
LEFT JOIN adm_organization o ON cs.org_id = o.id
|
||||
LEFT JOIN adm_encounter ae ON ae.id = cs.encounter_id AND ae.delete_flag = '0'
|
||||
LEFT JOIN adm_account aa ON aa.encounter_id = ae.id AND aa.delete_flag = '0'
|
||||
LEFT JOIN fin_contract fc ON fc.bus_no = aa.contract_no AND fc.delete_flag = '0'
|
||||
LEFT JOIN doc_request_form drf ON drf.prescription_no=cs.surgery_no
|
||||
LEFT JOIN (
|
||||
SELECT patient_id, identifier_no
|
||||
|
||||
@@ -98,9 +98,6 @@
|
||||
INNER JOIN adm_organization_location AS T6 ON T6.distribution_category_code = t1.category_code AND T6.delete_flag = '0' AND T6.item_code = '1' AND T6.organization_id = #{organizationId} AND (CURRENT_TIME :: time (6) BETWEEN T6.start_time AND T6.end_time)
|
||||
WHERE t1.delete_flag = '0'
|
||||
AND T2.status_enum = #{statusEnum}
|
||||
<if test="pricingFlag == 1">
|
||||
AND 1 = 2
|
||||
</if>
|
||||
<if test="categoryCode != null and categoryCode != ''">
|
||||
<!-- 🔧 BugFix: 支持两种匹配方式 -->
|
||||
<!-- 1. 直接匹配:distribution_category_code = category_code(都是数字代码) -->
|
||||
@@ -530,6 +527,9 @@
|
||||
LEFT JOIN cli_condition AS cc ON cc.id = T1.condition_id AND cc.delete_flag = '0'
|
||||
LEFT JOIN cli_condition_definition AS ccd ON ccd.id = cc.definition_id
|
||||
WHERE T1.delete_flag = '0' AND T1.tcm_flag = 0
|
||||
<if test="generateSourceEnum != null">
|
||||
AND T1.generate_source_enum = #{generateSourceEnum}
|
||||
</if>
|
||||
<if test="historyFlag == '0'.toString()">
|
||||
AND T1.encounter_id = #{encounterId}
|
||||
</if>
|
||||
@@ -544,9 +544,9 @@
|
||||
CI.service_id AS request_id,
|
||||
CI.service_id || '-ci-dev' AS unique_key,
|
||||
'' AS prescription_no,
|
||||
CI.enterer_id AS requester_id,
|
||||
COALESCE(DR.requester_id, CI.enterer_id) AS requester_id,
|
||||
CI.entered_date AS request_time,
|
||||
CASE WHEN CI.enterer_id = #{practitionerId} THEN '1' ELSE '0' END AS biz_request_flag,
|
||||
CASE WHEN COALESCE(DR.requester_id, CI.enterer_id) = #{practitionerId} THEN '1' ELSE '0' END AS biz_request_flag,
|
||||
DR.content_json AS content_json,
|
||||
NULL AS skin_test_flag,
|
||||
NULL AS inject_flag,
|
||||
@@ -698,6 +698,9 @@
|
||||
-- based_on_table='med_medication_request' → 输液/皮试执行记录,应排除
|
||||
-- based_on_table='exam_apply'/'lab_apply' → 申请单原始医嘱,应保留
|
||||
AND (T1.based_on_id IS NULL OR T1.based_on_table IS NULL OR T1.based_on_table NOT IN ('med_medication_request', 'med_medication_dispense'))
|
||||
<if test="sourceBillNo != null and sourceBillNo != ''">
|
||||
AND T1.prescription_no = #{sourceBillNo}
|
||||
</if>
|
||||
<if test="historyFlag == '0'.toString()">
|
||||
AND T1.encounter_id = #{encounterId}
|
||||
</if>
|
||||
|
||||
@@ -231,7 +231,7 @@
|
||||
ae.priority_enum,
|
||||
ae.organization_id,
|
||||
ae.start_time AS in_hos_time,
|
||||
bed.start_time,
|
||||
COALESCE(bed.start_time, ae.start_time) AS start_time,
|
||||
bed.location_id AS bed_id,
|
||||
bed.location_name AS bed_name,
|
||||
house.location_id AS house_id,
|
||||
|
||||
@@ -153,7 +153,8 @@
|
||||
ii.balance_amount AS balance_amount,
|
||||
ii.account_id AS account_id,
|
||||
ii.performer_check_id,
|
||||
ii.category_code
|
||||
ii.category_code,
|
||||
ii.dispense_status
|
||||
FROM (( SELECT T1.encounter_id,
|
||||
T1.tenant_id,
|
||||
#{medMedicationRequest} AS advice_table,
|
||||
@@ -197,7 +198,8 @@
|
||||
pra."name" AS admitting_doctor_name,
|
||||
personal_account.balance_amount,
|
||||
personal_account.id AS account_id,
|
||||
T2.category_code
|
||||
T2.category_code,
|
||||
mmd.status_enum AS dispense_status
|
||||
FROM med_medication_request AS T1
|
||||
LEFT JOIN med_medication_definition AS T2
|
||||
ON T2.id = T1.medication_id
|
||||
@@ -278,6 +280,9 @@
|
||||
aa.balance_amount
|
||||
) AS personal_account
|
||||
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'
|
||||
AND T1.refund_medicine_id IS NULL
|
||||
AND T1.generate_source_enum = #{doctorPrescription}
|
||||
@@ -331,7 +336,8 @@
|
||||
pra."name" AS admitting_doctor_name,
|
||||
personal_account.balance_amount,
|
||||
personal_account.id AS account_id,
|
||||
T2.category_code
|
||||
T2.category_code,
|
||||
NULL AS dispense_status
|
||||
FROM wor_service_request AS T1
|
||||
LEFT JOIN wor_activity_definition AS T2
|
||||
ON T2.id = T1.activity_id
|
||||
|
||||
@@ -105,6 +105,8 @@
|
||||
<if test="statusEnum == 4">
|
||||
T4.status_enum = #{completed}
|
||||
</if>
|
||||
AND T4.summary_no IS NOT NULL
|
||||
AND T4.summary_no != ''
|
||||
) AS ii
|
||||
${ew.customSqlSegment}
|
||||
GROUP BY ii.encounter_id,
|
||||
@@ -263,6 +265,8 @@
|
||||
AND T15.delete_flag = '0'
|
||||
WHERE T1.delete_flag = '0'
|
||||
-- 因发药配药合并,前台只能看到待发药,已发药状态,但是后台配药发药状态都查
|
||||
AND T1.summary_no IS NOT NULL
|
||||
AND T1.summary_no != ''
|
||||
AND
|
||||
<if test="dispenseStatus == null">
|
||||
T1.status_enum IN (#{inProgress},#{completed},#{preparation},#{prepared})
|
||||
|
||||
@@ -216,7 +216,8 @@
|
||||
ccd.name AS condition_definition_name,
|
||||
T1.therapy_enum AS therapyEnum,
|
||||
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
|
||||
LEFT JOIN med_medication_definition AS T2 ON T2.ID = T1.medication_id
|
||||
AND T2.delete_flag = '0'
|
||||
@@ -268,7 +269,8 @@
|
||||
'' AS condition_definition_name,
|
||||
2 AS therapyEnum,
|
||||
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
|
||||
LEFT JOIN adm_device_definition AS T2 ON T2.ID = T1.device_def_id
|
||||
AND T2.delete_flag = '0'
|
||||
@@ -286,7 +288,7 @@
|
||||
AND T1.refund_device_id IS NULL
|
||||
ORDER BY T1.status_enum)
|
||||
UNION ALL
|
||||
(SELECT CASE WHEN T1.category_enum = 4 THEN 6 ELSE COALESCE(T1.category_enum, 3) END AS advice_type,
|
||||
(SELECT CASE WHEN T1.category_enum = 4 THEN 6 ELSE 3 END AS advice_type,
|
||||
T1.id AS request_id,
|
||||
T1.id || '-3' AS unique_key,
|
||||
T1.requester_id AS requester_id,
|
||||
@@ -317,7 +319,8 @@
|
||||
'' AS condition_definition_name,
|
||||
COALESCE(T1.therapy_enum, 2) AS therapyEnum,
|
||||
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
|
||||
LEFT JOIN wor_activity_definition AS T2
|
||||
ON T2.ID = T1.activity_id
|
||||
@@ -370,4 +373,4 @@
|
||||
</if>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
</mapper>
|
||||
|
||||
@@ -12,13 +12,24 @@
|
||||
drf.desc_json,
|
||||
drf.requester_id,
|
||||
drf.create_time,
|
||||
drf.status,
|
||||
ap.NAME AS patient_name
|
||||
ap.NAME AS patient_name,
|
||||
CASE MIN(wsr.status_enum)
|
||||
WHEN 1 THEN 0
|
||||
WHEN 2 THEN 1
|
||||
WHEN 3 THEN 4
|
||||
WHEN 4 THEN 4
|
||||
WHEN 5 THEN 5
|
||||
WHEN 6 THEN 5
|
||||
WHEN 7 THEN 5
|
||||
ELSE NULL
|
||||
END AS status
|
||||
FROM doc_request_form AS drf
|
||||
LEFT JOIN adm_encounter AS ae ON ae.ID = drf.encounter_id
|
||||
AND ae.delete_flag = '0'
|
||||
LEFT JOIN adm_patient AS ap ON ap.ID = ae.patient_id
|
||||
AND ap.delete_flag = '0'
|
||||
LEFT JOIN wor_service_request AS wsr ON wsr.prescription_no = drf.prescription_no
|
||||
AND wsr.delete_flag = '0'
|
||||
WHERE drf.delete_flag = '0'
|
||||
AND drf.encounter_id = #{encounterId}
|
||||
AND drf.type_code = #{typeCode}
|
||||
@@ -29,8 +40,32 @@
|
||||
AND drf.create_time <= (#{endDate}::date + INTERVAL '1 day' - INTERVAL '1 second')
|
||||
</if>
|
||||
<if test="status != null and status != ''">
|
||||
AND drf.status = #{status}::integer
|
||||
AND CASE MIN(wsr.status_enum)
|
||||
WHEN 1 THEN 0
|
||||
WHEN 2 THEN 1
|
||||
WHEN 3 THEN 4
|
||||
WHEN 4 THEN 4
|
||||
WHEN 5 THEN 5
|
||||
WHEN 6 THEN 5
|
||||
WHEN 7 THEN 5
|
||||
ELSE NULL
|
||||
END = #{status}::integer
|
||||
</if>
|
||||
<if test="keyword != null and keyword != ''">
|
||||
AND (drf.prescription_no ILIKE '%' || #{keyword} || '%'
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM wor_service_request wsr2
|
||||
WHERE wsr2.prescription_no = drf.prescription_no
|
||||
AND wsr2.delete_flag = '0'
|
||||
AND wsr2.activity_id IN (
|
||||
SELECT id FROM wor_activity_definition wad
|
||||
WHERE wad.delete_flag = '0'
|
||||
AND wad.name ILIKE '%' || #{keyword} || '%'
|
||||
)
|
||||
))
|
||||
</if>
|
||||
GROUP BY drf.id, drf.encounter_id, drf.prescription_no, drf.name, drf.desc_json,
|
||||
drf.requester_id, drf.create_time, ap.name
|
||||
</select>
|
||||
|
||||
<select id="getRequestFormDetail" resultType="com.openhis.web.regdoctorstation.dto.RequestFormDetailQueryDto">
|
||||
@@ -126,6 +161,9 @@
|
||||
<if test="requestFormDto.surgeryNo != null and requestFormDto.surgeryNo != ''">
|
||||
AND drf.prescription_no LIKE CONCAT('%', #{requestFormDto.surgeryNo}, '%')
|
||||
</if>
|
||||
<if test="requestFormDto.typeCode != null and requestFormDto.typeCode != ''">
|
||||
AND drf.type_code = #{requestFormDto.typeCode}
|
||||
</if>
|
||||
<if test="requestFormDto.applyTimeStart != null">
|
||||
AND drf.create_time >= #{requestFormDto.applyTimeStart}
|
||||
</if>
|
||||
|
||||
@@ -270,6 +270,10 @@ public enum AssignSeqEnum {
|
||||
* 诊疗处方号
|
||||
*/
|
||||
ACTIVITY_PSYCHOTROPIC_NO("62", "诊疗处方号", "PAR"),
|
||||
/**
|
||||
* 检查申请单号(住院)
|
||||
*/
|
||||
CHECK_APPLY_NO("72", "检查申请单号", "JCZ"),
|
||||
/**
|
||||
* b 病历文书
|
||||
*/
|
||||
|
||||
@@ -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.mapper.OrderMapper;
|
||||
import com.openhis.clinical.service.IOrderService;
|
||||
import com.openhis.common.constant.CommonConstants.AppointmentOrderStatus;
|
||||
import com.openhis.common.enums.AssignSeqEnum;
|
||||
import com.openhis.common.enums.OrderStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -124,7 +124,8 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
||||
: new Date(); // 兜底:正常业务不应走到这里
|
||||
order.setAppointmentDate(appointmentDateTime);
|
||||
order.setAppointmentTime(appointmentDateTime);
|
||||
order.setStatus(AppointmentOrderStatus.BOOKED);
|
||||
// 订单状态: 0=患者取消 1=有效 2=系统取消 3=已完成
|
||||
order.setStatus(OrderStatus.ACTIVE.getValue());
|
||||
order.setPayStatus(0);
|
||||
order.setVersion(0);
|
||||
|
||||
@@ -169,10 +170,13 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
||||
if (order == null) {
|
||||
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("订单已取消");
|
||||
}
|
||||
if (AppointmentOrderStatus.CHECKED_IN.equals(order.getStatus())) {
|
||||
// 已完成(3)的订单不可取消
|
||||
if (OrderStatus.COMPLETED.getValue().equals(order.getStatus())) {
|
||||
throw new RuntimeException("订单已完成,无法取消");
|
||||
}
|
||||
|
||||
@@ -189,6 +193,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
|
||||
.eq(Order::getPatientId, patientId)
|
||||
.eq(Order::getTenantId, tenantId)
|
||||
.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.service.IOrderService;
|
||||
import com.openhis.clinical.service.ITicketService;
|
||||
import com.openhis.common.constant.CommonConstants.AppointmentOrderStatus;
|
||||
import com.openhis.common.constant.CommonConstants.SlotStatus;
|
||||
import com.openhis.common.enums.OrderStatus;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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 endTime = Date.from(periodEnd.atZone(ZoneId.systemDefault()).toInstant());
|
||||
|
||||
// 预约去重以订单为准(order_main),因为预约成功会先落订单;clinical_ticket 不一定在此链路写入
|
||||
List<Integer> effectiveOrderStatuses = Arrays.asList(AppointmentOrderStatus.BOOKED, AppointmentOrderStatus.CHECKED_IN);
|
||||
// 预约去重以订单为准(order_main),有效订单(1)才参与去重
|
||||
List<Integer> effectiveOrderStatuses = Arrays.asList(OrderStatus.ACTIVE.getValue());
|
||||
int exists = orderMapper.countPatientDeptOrdersInPeriod(dto.getPatientId(), slot.getDepartmentId(), slot.getDepartmentName(),
|
||||
startTime, endTime, effectiveOrderStatuses);
|
||||
if (exists > 0) {
|
||||
@@ -314,9 +314,8 @@ public class TicketServiceImpl extends ServiceImpl<TicketMapper, Ticket> impleme
|
||||
}
|
||||
Order latestOrder = orders.get(0);
|
||||
|
||||
// 1. 更新订单状态为已取号,并更新支付状态和支付时间
|
||||
orderService.updateOrderStatusById(latestOrder.getId(), AppointmentOrderStatus.CHECKED_IN);
|
||||
// 更新支付状态为已支付,记录支付时间
|
||||
// 1. 签到不改变订单状态(仍为有效1),更新支付状态为已支付并记录支付时间
|
||||
orderService.updateOrderStatusById(latestOrder.getId(), OrderStatus.ACTIVE.getValue());
|
||||
orderMapper.updatePayStatus(latestOrder.getId(), 1, new Date());
|
||||
|
||||
// 2. 查询号源槽位信息
|
||||
|
||||
@@ -187,6 +187,12 @@ public class OpSchedule extends HisBaseEntity {
|
||||
/** 沟通信息 */
|
||||
private String communicationInfo;
|
||||
|
||||
/** 是否外请专家 1-是 0-否 */
|
||||
private Integer isExternalExpert;
|
||||
|
||||
/** 外请专家姓名 */
|
||||
private String externalExpertName;
|
||||
|
||||
/** 备注信息 */
|
||||
private String remark;
|
||||
|
||||
|
||||
@@ -160,11 +160,12 @@
|
||||
AND delete_flag = '0'
|
||||
</update>
|
||||
|
||||
<!-- status=0(待约)时清空order_id,释放号源,使退号后号源可再被预约 -->
|
||||
<update id="updateSlotStatus">
|
||||
UPDATE adm_schedule_slot
|
||||
SET
|
||||
status = #{status},
|
||||
<if test="status != null and '0'.equals(status.toString())">
|
||||
<if test="status != null and status == 0">
|
||||
order_id = NULL,
|
||||
</if>
|
||||
update_time = now()
|
||||
|
||||
@@ -117,12 +117,14 @@
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<!-- status=1: 只查有效订单(0=患者取消 1=有效 2=系统取消 3=已完成) -->
|
||||
<select id="selectOrderById" resultMap="OrderResult">
|
||||
select * from order_main where id = #{id}
|
||||
and status = 1
|
||||
order by create_time desc
|
||||
</select>
|
||||
|
||||
<!-- status=1: 只查有效订单 -->
|
||||
<select id="selectOrderBySlotId" resultMap="OrderResult">
|
||||
select * from order_main where slot_id = #{slotId} and status = 1
|
||||
</select>
|
||||
@@ -248,8 +250,9 @@
|
||||
update order_main set status = #{status} where id = #{id}
|
||||
</update>
|
||||
|
||||
<!-- status=0: 患者取消 (OrderStatus.PATIENT_CANCELLED) -->
|
||||
<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 id="updatePayStatus">
|
||||
|
||||
@@ -13,7 +13,7 @@ CREATE TABLE IF NOT EXISTS triage_queue_item (
|
||||
patient_name VARCHAR(255),
|
||||
healthcare_name VARCHAR(255),
|
||||
practitioner_name VARCHAR(255),
|
||||
status VARCHAR(50) NOT NULL, -- WAITING/CALLING/SKIPPED/COMPLETED
|
||||
status INTEGER NOT NULL DEFAULT 0, -- 分诊队列状态: 0=WAITING(等待), 10=CALLING(叫号中), 20=IN_CLINIC(诊中), 30=COMPLETED(已完成), 40=SKIPPED(已跳过)
|
||||
queue_order INTEGER NOT NULL,
|
||||
create_time TIMESTAMP,
|
||||
update_time TIMESTAMP,
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
-- Bug #400 修复:triage_queue_item.status 字段类型修正
|
||||
-- 原 DDL 将 status 定义为 VARCHAR(50),但 Java 实体 TriageQueueItem.status 为 Integer 类型,
|
||||
-- 应用层使用 TriageQueueStatus 枚举值(0/10/20/30/40)写入,类型不匹配导致 MyBatis-Plus
|
||||
-- 无法正确映射 status 字段,完诊时 status 更新为 30 失败。
|
||||
--
|
||||
-- 注意:必须在后端实际连接的 schema 中执行(dev=hisdev, test=histest, prd=hisprd)
|
||||
-- 执行前请先确认:SET search_path TO hisdev; (或对应的 schema)
|
||||
|
||||
-- 1. 先将已有的字符串值转换为对应的整数值
|
||||
-- 如果之前写入的是枚举 code(如 'waiting'、'completed'),需要先转换
|
||||
UPDATE triage_queue_item
|
||||
SET status = CASE
|
||||
WHEN status IN ('0', 'waiting', 'WAITING') THEN 0
|
||||
WHEN status IN ('10', 'calling', 'CALLING') THEN 10
|
||||
WHEN status IN ('20', 'in-clinic', 'IN_CLINIC', 'in-clinic') THEN 20
|
||||
WHEN status IN ('30', 'completed', 'COMPLETED') THEN 30
|
||||
WHEN status IN ('40', 'skipped', 'SKIPPED') THEN 40
|
||||
WHEN status IN ('50', 'refunded', 'REFUNDED') THEN 50
|
||||
WHEN status IN ('60', 'follow', 'FOLLOW') THEN 60
|
||||
ELSE 0
|
||||
END
|
||||
WHERE status IS NOT NULL AND status !~ '^[0-9]+$';
|
||||
|
||||
-- 2. 修改字段类型为 INTEGER
|
||||
ALTER TABLE triage_queue_item
|
||||
ALTER COLUMN status TYPE INTEGER USING status::INTEGER;
|
||||
|
||||
-- 3. 设置默认值
|
||||
ALTER TABLE triage_queue_item
|
||||
ALTER COLUMN status SET DEFAULT 0;
|
||||
@@ -38,7 +38,6 @@ export function listAdviceTypes(query) {
|
||||
params: query
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
/**
|
||||
* 查询手术室详细
|
||||
|
||||
@@ -163,7 +163,7 @@ export function updateCheckPart(data) {
|
||||
// 查询检查套餐列表
|
||||
export function listCheckPackage(query) {
|
||||
return request({
|
||||
url: '/system/check-package/list',
|
||||
url: '/system/package/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
|
||||
@@ -372,7 +372,6 @@ import {
|
||||
} from './diagnosistreatment';
|
||||
import PopoverList from '@/components/OpenHis/popoverList/index.vue';
|
||||
import medicineList from './medicineList.vue';
|
||||
import MedicineList from '../components/medicineList.vue';
|
||||
import {getCurrentInstance, nextTick, watch} from 'vue';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
@@ -467,16 +466,24 @@ function calculateTotalPrice() {
|
||||
try {
|
||||
let sum = 0;
|
||||
treatmentItems.value.forEach((item) => {
|
||||
if (item.adviceDefinitionId && item.retailPrice && item.childrenRequestNum) {
|
||||
const price = parseFloat(item.retailPrice) || 0;
|
||||
const count = parseInt(item.childrenRequestNum) || 0;
|
||||
if (item.adviceDefinitionId && item.adviceDefinitionId !== '') {
|
||||
const price = Number(item.retailPrice) || 0;
|
||||
const count = Number(item.childrenRequestNum) || 0;
|
||||
sum += price * count;
|
||||
}
|
||||
});
|
||||
totalPrice.value = sum.toFixed(2);
|
||||
// Bug #464: 零售价与诊疗子项合计总价实时同步
|
||||
if (treatmentItems.value.length > 0 && treatmentItems.value[0].adviceDefinitionId !== '') {
|
||||
form.value.retailPrice = parseFloat(totalPrice.value);
|
||||
const hasValidItem = treatmentItems.value.some(
|
||||
(item) => item.adviceDefinitionId && item.adviceDefinitionId !== ''
|
||||
);
|
||||
if (hasValidItem) {
|
||||
// 使用 nextTick 确保总价更新后零售价才更新,避免 Vue 响应式时序问题
|
||||
nextTick(() => {
|
||||
form.value.retailPrice = parseFloat(totalPrice.value) || 0;
|
||||
});
|
||||
} else {
|
||||
form.value.retailPrice = undefined;
|
||||
}
|
||||
} catch (error) {
|
||||
totalPrice.value = '0.00';
|
||||
@@ -486,7 +493,7 @@ function calculateTotalPrice() {
|
||||
|
||||
// 添加表单项
|
||||
function addItem() {
|
||||
treatmentItems.value.push({ adviceDefinitionId: '', childrenRequestNum: 1, retailPrice: 0 });
|
||||
treatmentItems.value.push({ adviceDefinitionId: '', childrenRequestNum: 1, name: '', retailPrice: 0 });
|
||||
// 使用nextTick确保DOM更新后再计算
|
||||
nextTick(() => {
|
||||
calculateTotalPrice();
|
||||
@@ -560,15 +567,16 @@ function edit() {
|
||||
form.value.pricingFlag = 1;
|
||||
}
|
||||
|
||||
// 处理子项数据,确保包含retailPrice字段
|
||||
// 处理子项数据,确保包含retailPrice和name字段
|
||||
if (props.item.childrenJson) {
|
||||
const parsedItems = JSON.parse(props.item.childrenJson);
|
||||
treatmentItems.value = parsedItems.map((item) => ({
|
||||
...item,
|
||||
name: item.name || '',
|
||||
retailPrice: item.retailPrice || 0,
|
||||
}));
|
||||
} else {
|
||||
treatmentItems.value = [{ adviceDefinitionId: '', childrenRequestNum: 1, retailPrice: 0 }];
|
||||
treatmentItems.value = [{ adviceDefinitionId: '', childrenRequestNum: 1, name: '', retailPrice: 0 }];
|
||||
}
|
||||
form.value.permittedUnitCode = form.value.permittedUnitCode
|
||||
? form.value.permittedUnitCode.toString()
|
||||
@@ -613,7 +621,7 @@ function reset() {
|
||||
chrgitmLv: undefined, //医保等级
|
||||
pricingFlag: 1, // 划价标记,默认允许划价
|
||||
};
|
||||
treatmentItems.value = [{ adviceDefinitionId: '', childrenRequestNum: 1, retailPrice: 0 }];
|
||||
treatmentItems.value = [{ adviceDefinitionId: '', childrenRequestNum: 1, name: '', retailPrice: 0 }];
|
||||
totalPrice.value = '0.00';
|
||||
proxy.resetForm('diagnosisTreatmentRef');
|
||||
}
|
||||
@@ -647,12 +655,15 @@ async function submitForm() {
|
||||
form.value.ybMatchFlag ? (form.value.ybMatchFlag = 1) : (form.value.ybMatchFlag = 0);
|
||||
form.value.ruleId ? (form.value.ruleId = 1) : (form.value.ruleId = 0);
|
||||
form.value.childrenJson =
|
||||
treatmentItems.value.length > 0 && treatmentItems.value[0].adviceDefinitionId != ''
|
||||
treatmentItems.value.some((item) => item.adviceDefinitionId != '' && item.adviceDefinitionId)
|
||||
? JSON.stringify(treatmentItems.value)
|
||||
: undefined;
|
||||
// Bug #464 修复:零售价自动与诊疗子项合计总价同步
|
||||
// 当有子项时,零售价自动设置为子项合计总价
|
||||
if (treatmentItems.value.length > 0 && treatmentItems.value[0].adviceDefinitionId != '') {
|
||||
const hasValidItem = treatmentItems.value.some(
|
||||
(item) => item.adviceDefinitionId && item.adviceDefinitionId !== ''
|
||||
);
|
||||
if (hasValidItem) {
|
||||
form.value.retailPrice = parseFloat(totalPrice.value) || 0;
|
||||
}
|
||||
proxy.$refs['diagnosisTreatmentRef'].validate(async (valid) => {
|
||||
@@ -752,7 +763,10 @@ function selectRow(row, index) {
|
||||
treatmentItems.value[index].adviceDefinitionId = row.id;
|
||||
treatmentItems.value[index].retailPrice = row.retailPrice || 0;
|
||||
medicineSearchKey.value = '';
|
||||
calculateTotalPrice();
|
||||
// 使用 nextTick 确保 DOM 更新后再计算总价
|
||||
nextTick(() => {
|
||||
calculateTotalPrice();
|
||||
});
|
||||
}
|
||||
|
||||
// 清空诊疗子项
|
||||
|
||||
@@ -36,6 +36,7 @@ const emit = defineEmits(['selectRow']);
|
||||
const diagnosisTreatmentList = ref([]); // 原始数据列表
|
||||
const filteredList = ref([]); // 过滤后的数据列表
|
||||
const hasLoaded = ref(false); // 标记是否已加载数据
|
||||
const isLoading = ref(false); // 标记是否正在加载
|
||||
|
||||
// 获取诊疗项目列表
|
||||
function getList() {
|
||||
@@ -53,7 +54,7 @@ function getList() {
|
||||
getDiagnosisTreatmentList({ statusEnum: 2, pageSize: 1000, pageNo: 1 })
|
||||
.then((res) => {
|
||||
diagnosisTreatmentList.value =
|
||||
res.data?.records?.filter((item) => item.childrenJson == null) || [];
|
||||
res.data?.records?.filter((item) => item.childrenJson == null || item.childrenJson === '') || [];
|
||||
filterList(); // 初始化过滤
|
||||
hasLoaded.value = true; // 标记为已加载
|
||||
})
|
||||
@@ -62,6 +63,47 @@ function getList() {
|
||||
});
|
||||
}
|
||||
|
||||
// 服务端搜索(当用户输入搜索关键词时)
|
||||
function searchList(searchKey) {
|
||||
if (!searchKey || searchKey.trim() === '') return;
|
||||
isLoading.value = true;
|
||||
// 使用较大的pageSize确保搜索结果尽可能多
|
||||
getDiagnosisTreatmentList({ statusEnum: 2, searchKey: searchKey.trim(), pageSize: 5000, pageNo: 1 })
|
||||
.then((res) => {
|
||||
diagnosisTreatmentList.value =
|
||||
res.data?.records?.filter((item) => item.childrenJson == null || item.childrenJson === '') || [];
|
||||
filterList();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('搜索诊疗项目数据失败:', err);
|
||||
})
|
||||
.finally(() => {
|
||||
isLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
// 获取预加载数据(不带搜索关键词时使用)
|
||||
function loadPreloadedData() {
|
||||
if (props.preloadedData && props.preloadedData.length > 0) {
|
||||
diagnosisTreatmentList.value = props.preloadedData;
|
||||
filterList();
|
||||
hasLoaded.value = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasLoaded.value) return;
|
||||
getDiagnosisTreatmentList({ statusEnum: 2, pageSize: 1000, pageNo: 1 })
|
||||
.then((res) => {
|
||||
diagnosisTreatmentList.value =
|
||||
res.data?.records?.filter((item) => item.childrenJson == null || item.childrenJson === '') || [];
|
||||
filterList();
|
||||
hasLoaded.value = true;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('获取诊疗项目数据失败:', err);
|
||||
});
|
||||
}
|
||||
|
||||
// 监听shouldLoadData属性变化,仅在首次为true时加载数据
|
||||
watch(
|
||||
() => props.shouldLoadData,
|
||||
@@ -86,11 +128,17 @@ watch(
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
// 监听搜索关键词变化,实时过滤数据
|
||||
// 监听搜索关键词变化,有搜索词时走服务端搜索,否则走本地过滤
|
||||
watch(
|
||||
() => props.searchKey,
|
||||
() => {
|
||||
filterList();
|
||||
(newVal) => {
|
||||
if (newVal && newVal.trim() !== '') {
|
||||
// 有搜索关键词,走服务端搜索
|
||||
searchList(newVal);
|
||||
} else {
|
||||
// 搜索词为空,使用预加载数据
|
||||
loadPreloadedData();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -69,13 +69,14 @@ const throttledGetList = throttle(
|
||||
watch(
|
||||
() => props.adviceQueryParams,
|
||||
(newValue) => {
|
||||
// 只有在弹窗打开时才响应 adviceQueryParams 的变化,避免选择项目后弹窗关闭时触发不必要的请求
|
||||
if (!props.popoverVisible) {
|
||||
return;
|
||||
}
|
||||
// 始终同步参数到 queryParams,避免弹窗打开时使用旧参数
|
||||
queryParams.value.searchKey = newValue?.searchKey;
|
||||
queryParams.value.adviceType = newValue?.adviceType;
|
||||
queryParams.value.categoryCode = newValue?.categoryCode;
|
||||
// 只有在弹窗打开时才触发 API 请求
|
||||
if (!props.popoverVisible) {
|
||||
return;
|
||||
}
|
||||
throttledGetList();
|
||||
},
|
||||
{ deep: true }
|
||||
|
||||
@@ -58,10 +58,17 @@ export function singOut(data) {
|
||||
/**
|
||||
* 获取患者本次就诊处方
|
||||
*/
|
||||
export function getPrescriptionList(encounterId) {
|
||||
// Add timestamp to bypass browser caching and ensure fresh data is loaded
|
||||
export function getPrescriptionList(encounterId, generateSourceEnum, sourceBillNo) {
|
||||
let url = '/doctor-station/advice/request-base-info?encounterId=' + encounterId
|
||||
if (generateSourceEnum != null) {
|
||||
url += '&generateSourceEnum=' + generateSourceEnum
|
||||
}
|
||||
if (sourceBillNo != null) {
|
||||
url += '&sourceBillNo=' + encodeURIComponent(sourceBillNo)
|
||||
}
|
||||
url += '&t=' + Date.now()
|
||||
return request({
|
||||
url: '/doctor-station/advice/request-base-info?encounterId=' + encounterId + '&t=' + Date.now(),
|
||||
url: url,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -381,6 +381,14 @@ const props = defineProps({
|
||||
activeTab: {
|
||||
type: String,
|
||||
},
|
||||
generateSourceEnum: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
sourceBillNo: {
|
||||
type: String,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
const isAdding = ref(false);
|
||||
const isSaving = ref(false); // #437 防重复提交锁
|
||||
@@ -468,7 +476,11 @@ watch(
|
||||
|
||||
function getListInfo(addNewRow) {
|
||||
isAdding.value = false;
|
||||
getPrescriptionList(props.patientInfo.encounterId).then((res) => {
|
||||
getPrescriptionList(
|
||||
props.patientInfo.encounterId,
|
||||
props.generateSourceEnum ?? undefined,
|
||||
props.sourceBillNo ?? undefined,
|
||||
).then((res) => {
|
||||
// 为每行数据添加 adviceTypeValue 字段,用于类型下拉框显示
|
||||
prescriptionList.value = (res.data || []).map(item => {
|
||||
// 根据 adviceType 和 categoryCode 找到对应的 adviceTypeValue
|
||||
@@ -868,19 +880,23 @@ function handleDelete() {
|
||||
proxy.$modal.msgWarning('请选择要删除的项目');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let deleteList = groupIndexList.value.map((index) => {
|
||||
const item = prescriptionList.value[index];
|
||||
// 只删除待签发且未收费的项目
|
||||
if (item.statusEnum != 1 || item.chargeStatus == 5) {
|
||||
return null;
|
||||
}
|
||||
// 🔧 Bug #442: 已保存的行必须有有效的 requestId,否则跳过(避免后端删除不存在的记录)
|
||||
if (item.requestId == null || item.requestId === undefined || item.requestId === '') {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
requestId: item.requestId,
|
||||
dbOpType: '3',
|
||||
adviceType: item.adviceType,
|
||||
};
|
||||
}).filter(item => item !== null); // 过滤掉已签发或已收费的项目
|
||||
}).filter(item => item !== null); // 过滤掉已签发、已收费或无 requestId 的项目
|
||||
|
||||
if (deleteList.length == 0) {
|
||||
proxy.$modal.msgWarning('只能删除待签发且未收费的项目');
|
||||
@@ -1146,7 +1162,13 @@ function calculateTotalPrice(row, index) {
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({ getListInfo });
|
||||
function closeAllPopovers() {
|
||||
prescriptionList.value.forEach(row => {
|
||||
row.showPopover = false;
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({ getListInfo, closeAllPopovers });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -519,9 +519,10 @@
|
||||
>
|
||||
<template #append>
|
||||
<!-- 审核记录:查看/审核都展示,保证留痕可追溯 -->
|
||||
<!-- 修复:查看模式始终显示区块;审核模式始终显示区块(header + 空状态 或 timeline) -->
|
||||
<div
|
||||
class="audit-records-section"
|
||||
v-if="drawerMode === 'view' || (drawerMode === 'audit' && auditRecords.length > 0)"
|
||||
v-if="drawerMode === 'view' || drawerMode === 'audit'"
|
||||
>
|
||||
<h3 class="section-title">审核记录</h3>
|
||||
<el-timeline v-if="auditRecords.length > 0">
|
||||
|
||||
@@ -127,6 +127,7 @@ const queryParams = ref({
|
||||
pageSize: 100,
|
||||
pageNo: 1,
|
||||
adviceTypes: '1,2,3',
|
||||
categoryCode: '',
|
||||
});
|
||||
|
||||
// 节流函数 - 与V1.3一致:300ms,首次立即响应
|
||||
@@ -148,6 +149,7 @@ watch(
|
||||
} else {
|
||||
queryParams.value.adviceTypes = '1,2,3';
|
||||
}
|
||||
queryParams.value.categoryCode = newValue.categoryCode || '';
|
||||
throttledGetList();
|
||||
},
|
||||
{ deep: true }
|
||||
@@ -175,6 +177,12 @@ function getList() {
|
||||
const types = adviceTypes.split(',').map(t => parseInt(t));
|
||||
filteredData = filteredData.filter(item => types.includes(item.adviceType));
|
||||
}
|
||||
|
||||
// 根据 categoryCode 过滤(如西药='2',中成药='1')
|
||||
const categoryCode = queryParams.value.categoryCode;
|
||||
if (categoryCode) {
|
||||
filteredData = filteredData.filter(item => String(item.categoryCode) === String(categoryCode));
|
||||
}
|
||||
|
||||
// 根据搜索关键词过滤
|
||||
if (searchKey && searchKey.length >= 1) {
|
||||
|
||||
@@ -375,7 +375,7 @@ const finishCallPatient = async () => {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await completeEncounter(currentCallPatient.value.encounterId);
|
||||
await completeEncounter({ encounterId: currentCallPatient.value.encounterId, firstEnum: currentCallPatient.value.firstEnum || 1 });
|
||||
emit('finish');
|
||||
emit('update:dialogVisible', false);
|
||||
ElMessage.success('患者已完诊');
|
||||
|
||||
@@ -349,11 +349,17 @@ async function getList() {
|
||||
if (res.code == 200) {
|
||||
// 过滤掉中医诊断,只保留西医诊断
|
||||
form.value.diagnosisList = res.data.filter(item => item.typeName !== '中医诊断');
|
||||
// 为旧数据添加默认分类
|
||||
// 为旧数据添加默认分类和selectedDiseases
|
||||
form.value.diagnosisList.forEach(item => {
|
||||
if (!item.classification) {
|
||||
item.classification = '西医';
|
||||
}
|
||||
// 如果ybNo(诊断编码)符合传染病编码格式,添加到selectedDiseases
|
||||
if (item.ybNo && /^(01|02|03)/.test(item.ybNo)) {
|
||||
item.selectedDiseases = [item.ybNo];
|
||||
} else {
|
||||
item.selectedDiseases = item.selectedDiseases || [];
|
||||
}
|
||||
});
|
||||
emits('diagnosisSave', false);
|
||||
}
|
||||
@@ -685,23 +691,78 @@ async function handleFoodDiseasesCheck() {
|
||||
|
||||
/**
|
||||
* 传染病报告卡处理
|
||||
* 通过诊断目录维护的'报卡类型'字段自动识别是否有需要填写的传染病报告卡
|
||||
* 如果有则弹出诊断对应需登记的报告卡界面
|
||||
* 通过诊断名称自动识别并勾选传染病报告卡中的疾病
|
||||
*/
|
||||
function handleInfectiousDiseaseReport() {
|
||||
// 查找所有有报卡类型的诊断(reportTypeCode不为空)
|
||||
const diagnosesWithReportType = form.value.diagnosisList.filter(d => d.reportTypeCode);
|
||||
// 疾病名称到报卡编码的映射(根据传染病报告卡弹窗中的疾病列表)
|
||||
const diseaseNameToCode = {
|
||||
// 甲类
|
||||
'鼠疫': '0101',
|
||||
'霍乱': '0102',
|
||||
// 乙类
|
||||
'传染性非典型肺炎': '0201',
|
||||
'艾滋病': '0202',
|
||||
'病毒性肝炎': '0203',
|
||||
'脊髓灰质炎': '0204',
|
||||
'人感染高致病性禽流感': '0205',
|
||||
'麻疹': '0206',
|
||||
'流行性出血热': '0207',
|
||||
'狂犬病': '0208',
|
||||
'流行性乙型脑炎': '0209',
|
||||
'登革热': '0210',
|
||||
'炭疽': '0211',
|
||||
'细菌性和阿米巴性痢疾': '0212',
|
||||
'肺结核': '0213',
|
||||
'伤寒和副伤寒': '0214',
|
||||
'流行性脑脊髓膜炎': '0215',
|
||||
'百日咳': '0216',
|
||||
'白喉': '0217',
|
||||
'新生儿破伤风': '0218',
|
||||
'猩红热': '0219',
|
||||
'布鲁氏菌病': '0220',
|
||||
'淋病': '0221',
|
||||
'梅毒': '0222',
|
||||
'钩端螺旋体病': '0223',
|
||||
'血吸虫病': '0224',
|
||||
'疟疾': '0225',
|
||||
'新型冠状病毒肺炎': '0226',
|
||||
'甲型H1N1流感': '0227',
|
||||
'人感染H7N9禽流感': '0228',
|
||||
// 丙类
|
||||
'流行性感冒': '0301',
|
||||
'流行性腮腺炎': '0302',
|
||||
'风疹': '0303',
|
||||
'急性出血性结膜炎': '0304',
|
||||
'麻风病': '0305',
|
||||
'流行性和地方性斑疹伤寒': '0306',
|
||||
'黑热病': '0307',
|
||||
'包虫病': '0308',
|
||||
'丝虫病': '0309',
|
||||
'除霍乱/菌痢/伤寒副伤寒以外的感染性腹泻病': '0310',
|
||||
'其它感染性腹泻病': '0310',
|
||||
'手足口病': '0311',
|
||||
};
|
||||
|
||||
if (diagnosesWithReportType.length === 0) {
|
||||
// 获取所有诊断名称对应的报卡编码
|
||||
const allSelectedDiseases = form.value.diagnosisList
|
||||
.map(d => diseaseNameToCode[d.name] || null)
|
||||
.filter(code => code);
|
||||
|
||||
if (allSelectedDiseases.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 优先使用主诊断,如果没有主诊断有报卡类型则使用第一个有报卡类型的诊断
|
||||
const mainDiagnosisWithReport = diagnosesWithReportType.find(d => d.maindiseFlag === 1);
|
||||
const targetDiagnosis = mainDiagnosisWithReport || diagnosesWithReportType[0];
|
||||
// 优先使用主诊断
|
||||
const mainDiagnosis = form.value.diagnosisList.find(d => d.maindiseFlag === 1);
|
||||
const firstDiagnosis = form.value.diagnosisList[0];
|
||||
|
||||
const diagnosisToShow = {
|
||||
...(mainDiagnosis || firstDiagnosis),
|
||||
selectedDiseases: allSelectedDiseases
|
||||
};
|
||||
|
||||
// 弹出传染病报告卡弹窗
|
||||
proxy.$refs.infectiousDiseaseReportRef?.show(targetDiagnosis);
|
||||
proxy.$refs.infectiousDiseaseReportRef?.show(diagnosisToShow);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -814,7 +875,8 @@ form.value.diagnosisList.push({
|
||||
classification: '西医', // 默认为西医
|
||||
onsetDate: getCurrentDate(),
|
||||
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
|
||||
diagnosisTime: getCurrentDate()
|
||||
diagnosisTime: getCurrentDate(),
|
||||
selectedDiseases: data.ybNo ? [data.ybNo] : [], // 用于传染病报告卡自动勾选
|
||||
});
|
||||
|
||||
// 添加后按排序号排序
|
||||
|
||||
@@ -16,15 +16,15 @@
|
||||
v-model="form.cardNo"
|
||||
class="card-number-input"
|
||||
placeholder="单位自编,与网络直报一致"
|
||||
maxlength="12"
|
||||
:disabled="readOnly"
|
||||
maxlength="20"
|
||||
:disabled="readOnly || dialogReadOnly"
|
||||
/>
|
||||
</el-space>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<el-card class="report-form" shadow="never">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" :disabled="readOnly">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" :disabled="readOnly || dialogReadOnly">
|
||||
<!-- 患者姓名、家长姓名、身份证号 -->
|
||||
<el-row :gutter="16" class="form-row">
|
||||
<el-col :span="8" class="form-item">
|
||||
@@ -41,43 +41,9 @@
|
||||
</el-col>
|
||||
</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-col :span="24" class="form-item full-width">
|
||||
<el-col :span="12" class="form-item">
|
||||
<span class="form-label">工作单位(学校)</span>
|
||||
<el-input v-model="form.workplace" class="underline-input" />
|
||||
</el-col>
|
||||
@@ -510,9 +476,9 @@
|
||||
<template #footer>
|
||||
<slot name="footer" :close="handleClose" :submit-loading="submitLoading">
|
||||
<el-space :size="16" justify="center" class="dialog-footer-space" style="display: flex; justify-content: center; width: 100%;">
|
||||
<el-button v-if="!readOnly" type="primary" @click="handleSubmit" :loading="submitLoading" class="blue-button">保 存</el-button>
|
||||
<el-button v-if="!(readOnly || dialogReadOnly)" type="primary" @click="handleSubmit" :loading="submitLoading" class="blue-button">保 存</el-button>
|
||||
<el-button type="info" @click="handleClose">关 闭</el-button>
|
||||
<el-button v-if="!readOnly" type="danger" @click="handleReset">重 置</el-button>
|
||||
<el-button v-if="!(readOnly || dialogReadOnly)" type="danger" @click="handleReset">重 置</el-button>
|
||||
</el-space>
|
||||
</slot>
|
||||
</template>
|
||||
@@ -522,7 +488,7 @@
|
||||
<script setup>
|
||||
import { ref, computed, getCurrentInstance, watch } from 'vue';
|
||||
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 { useDict } from '@/utils/dict';
|
||||
|
||||
@@ -544,6 +510,7 @@ const DISEASE_NAMES = {
|
||||
};
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const dialogReadOnly = ref(false);
|
||||
const formRef = ref(null);
|
||||
// 保存按钮加载状态,防止重复提交
|
||||
const submitLoading = ref(false);
|
||||
@@ -1071,8 +1038,9 @@ function resetAddressSelector() {
|
||||
* 以只读详情方式打开报卡弹窗,供报卡管理等页面复用医生站报卡样式。
|
||||
* @param {Object} reportData - 报卡详情数据
|
||||
*/
|
||||
function showReport(reportData = {}) {
|
||||
function showReport(reportData = {}, readOnly = true) {
|
||||
dialogVisible.value = true;
|
||||
dialogReadOnly.value = readOnly;
|
||||
|
||||
resetAddressSelector();
|
||||
initProvinceOptions();
|
||||
@@ -1103,7 +1071,7 @@ function showReport(reportData = {}) {
|
||||
addressHouse: reportData.addressHouse || '',
|
||||
patientBelong: reportData.patientBelong || 1,
|
||||
occupation: reportData.occupation || '',
|
||||
caseClass: reportData.caseClass != null ? String(reportData.caseClass) : '',
|
||||
caseClass: reportData.diseaseType != null ? String(reportData.diseaseType) : '',
|
||||
onsetDate: normalizeDate(reportData.onsetDate),
|
||||
diagDate: normalizeDate(reportData.diagDate),
|
||||
deathDate: normalizeDate(reportData.deathDate),
|
||||
@@ -1112,13 +1080,13 @@ function showReport(reportData = {}) {
|
||||
selectedClassB: diseaseSelection.selectedClassB,
|
||||
selectedClassC: diseaseSelection.selectedClassC,
|
||||
otherDisease: reportData.otherDisease || (diseaseCode === 'OTHER' ? reportData.diseaseName || '' : ''),
|
||||
diseaseType: reportData.diseaseType || '',
|
||||
diseaseType: reportData.diseaseSubtype || '',
|
||||
reportOrg: reportData.reportOrg || '',
|
||||
reportOrgPhone: reportData.reportOrgPhone || '',
|
||||
reportDoc: reportData.reportDoc || '',
|
||||
reportDate: normalizeDate(reportData.reportDate || reportData.createdAt),
|
||||
correctName: reportData.correctName || '',
|
||||
withdrawReason: reportData.withdrawReason || '',
|
||||
correctName: reportData.revisedDiseaseName || '',
|
||||
withdrawReason: reportData.returnReason || '',
|
||||
remark: reportData.remark || '',
|
||||
encounterId: reportData.encounterId || reportData.visitId || '',
|
||||
patientId: reportData.patientId || reportData.patId || '',
|
||||
@@ -1237,8 +1205,9 @@ function calculateAge() {
|
||||
* 显示对话框并初始化表单数据
|
||||
* @param {Object} diagnosisData - 诊断数据,包含已选疾病和地址信息
|
||||
*/
|
||||
function show(diagnosisData) {
|
||||
async function show(diagnosisData) {
|
||||
dialogVisible.value = true;
|
||||
dialogReadOnly.value = false;
|
||||
|
||||
// 重置地址选择器状态
|
||||
resetAddressSelector();
|
||||
@@ -1267,12 +1236,26 @@ function show(diagnosisData) {
|
||||
});
|
||||
}
|
||||
|
||||
// 生成默认卡片编号:医疗机构编码+当前年月日+4位流水
|
||||
// 生成默认卡片编号:调用后端接口获取
|
||||
const orgCode = userStore.fixmedinsCode || '0000';
|
||||
let cardNo = '';
|
||||
try {
|
||||
const res = await getNextCardNo(orgCode);
|
||||
if (res.code === 200 && res.data && res.data.length >= 12) {
|
||||
cardNo = res.data;
|
||||
} else {
|
||||
// API返回失败或不合规时,生成临时卡号避免保存时 cardNo 为空导致后端校验失败
|
||||
cardNo = 'TEMP_' + Date.now();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('获取卡片编号失败:', err);
|
||||
// API调用异常时,生成临时卡号
|
||||
cardNo = 'TEMP_' + Date.now();
|
||||
}
|
||||
|
||||
form.value = {
|
||||
// 卡片编号:单位自编,与网络直报一致
|
||||
cardNo: '',
|
||||
cardNo: cardNo,
|
||||
|
||||
// 患者基本信息
|
||||
patName: patientInfo.patientName || patientInfo.name || '', // 患者姓名
|
||||
@@ -1338,7 +1321,11 @@ function show(diagnosisData) {
|
||||
// 系统关联信息
|
||||
encounterId: patientInfo.encounterId || '', // 就诊ID
|
||||
patientId: patientInfo.patientId || '', // 患者ID
|
||||
diagnosisId: diagnosisData?.conditionId || diagnosisData?.definitionId || '', // 诊断ID
|
||||
diagnosisId: (diagnosisData?.conditionId != null && diagnosisData?.conditionId !== '')
|
||||
? diagnosisData.conditionId
|
||||
: (diagnosisData?.definitionId != null && diagnosisData?.definitionId !== '')
|
||||
? diagnosisData.definitionId
|
||||
: '', // 诊断ID
|
||||
};
|
||||
|
||||
// 更新selectedDiseases数组
|
||||
@@ -1393,7 +1380,7 @@ async function buildSubmitData() {
|
||||
const submitData = {
|
||||
cardNo: formData.cardNo,
|
||||
visitId: props.patientInfo?.encounterId || formData.encounterId || null,
|
||||
diagId: formData.diagnosisId || null,
|
||||
diagId: formData.diagnosisId ? Number(formData.diagnosisId) : null,
|
||||
patId: formData.patientId || null,
|
||||
idType: 1, // 默认身份证
|
||||
idNo: formData.idNo,
|
||||
@@ -1446,9 +1433,9 @@ async function buildSubmitData() {
|
||||
function validateFormManually() {
|
||||
const errors = [];
|
||||
|
||||
// 卡片编号验证(可选,但如果填写了必须是12位)
|
||||
if (form.value.cardNo && form.value.cardNo.length !== 12) {
|
||||
errors.push('卡片编号必须为12位');
|
||||
// 卡片编号验证(至少12位,后端自动生成16位编号;临时卡号 TEMP_ 开头允许通过)
|
||||
if (form.value.cardNo && !form.value.cardNo.startsWith('TEMP_') && form.value.cardNo.length < 12) {
|
||||
errors.push('卡片编号至少12位');
|
||||
}
|
||||
|
||||
// 身份证号验证
|
||||
@@ -1559,6 +1546,12 @@ async function handleSubmit() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查诊断ID是否有效(后端 @NotNull 校验要求)
|
||||
if (!form.value.diagnosisId) {
|
||||
proxy.$modal.msgError('诊断信息不完整,请重新选择诊断后重试');
|
||||
return;
|
||||
}
|
||||
|
||||
// 开始加载状态,防止重复提交
|
||||
submitLoading.value = true;
|
||||
|
||||
|
||||
@@ -316,6 +316,7 @@
|
||||
<span class="cat-title">{{ cat.categoryName }}</span>
|
||||
<span v-if="categoryLoadingSet.has(cat.typeId)" class="loading-dot"></span>
|
||||
</template>
|
||||
<!-- 检查项目(部位/项目列表) -->
|
||||
<div
|
||||
v-for="item in cat.items"
|
||||
:key="item.id"
|
||||
@@ -333,6 +334,27 @@
|
||||
<div v-if="categoryLoadingSet.has(cat.typeId)" class="category-loading-hint">
|
||||
加载中...
|
||||
</div>
|
||||
<!-- Bug #428修复: 渲染分类联动加载的检查方法列表 -->
|
||||
<div
|
||||
v-if="cat.methods && cat.methods.length > 0"
|
||||
class="method-section"
|
||||
>
|
||||
<div class="method-section-title">检查方法</div>
|
||||
<div
|
||||
v-for="method in cat.methods"
|
||||
:key="method.id"
|
||||
class="method-row"
|
||||
>
|
||||
<el-checkbox
|
||||
:model-value="isMethodSelected(method, cat)"
|
||||
@change="(val) => handleMethodSelect(val, method, cat)"
|
||||
class="method-checkbox"
|
||||
>
|
||||
{{ method.name }}
|
||||
</el-checkbox>
|
||||
<span class="method-price-tag">¥{{ method.packagePrice || method.price || 0 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
@@ -380,6 +402,21 @@
|
||||
<span class="method-price">¥{{ method.packagePrice || item.price }}</span>
|
||||
</el-checkbox>
|
||||
</div>
|
||||
<!-- 选中方法后,显示对应的套餐明细 -->
|
||||
<div v-if="item.selectedMethod && item.methodPackageDetails && item.methodPackageDetails.length > 0" class="method-package-details">
|
||||
<div class="method-package-header">
|
||||
<span class="method-package-title">套餐明细 - {{ item.selectedMethod.name }}</span>
|
||||
</div>
|
||||
<div v-for="detail in item.methodPackageDetails" :key="detail.id" class="method-option">
|
||||
<el-checkbox v-model="detail.checked">
|
||||
<span class="method-name">{{ detail.name }}</span>
|
||||
<span class="method-price">数量: {{ detail.quantity }} ¥{{ detail.price }}</span>
|
||||
</el-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.selectedMethod && item.methodPackageLoading" class="method-package-loading">
|
||||
加载套餐明细中...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -397,7 +434,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Printer, Delete, ArrowDown, ArrowUp, Close } from '@element-plus/icons-vue';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import request from '@/utils/request';
|
||||
import { listCheckMethod, searchCheckMethod } from '@/api/system/checkType';
|
||||
import { listCheckMethod, searchCheckMethod, listCheckPackage } from '@/api/system/checkType';
|
||||
import { getEncounterDiagnosis } from '../api.js';
|
||||
|
||||
const props = defineProps({
|
||||
@@ -423,7 +460,7 @@ async function loadPackageDetails(row, treeNode, resolve) {
|
||||
}
|
||||
try {
|
||||
const res = await request({
|
||||
url: `/exam/package/${row.packageId}/details`,
|
||||
url: `/system/package/${row.packageId}/details`,
|
||||
method: 'get'
|
||||
});
|
||||
if (res.code === 200 && res.data) {
|
||||
@@ -445,14 +482,28 @@ async function loadPackageDetails(row, treeNode, resolve) {
|
||||
}
|
||||
}
|
||||
|
||||
// #428: 为已选择项目加载套餐明细
|
||||
// #428修复: 为已选择项目加载套餐明细(通过packageId或packageName查询)
|
||||
async function loadPackageDetailsForItem(item) {
|
||||
if (!item.isPackage || !item.packageId) {
|
||||
if (!item.isPackage || (!item.packageId && !item.packageName)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
let packageId = item.packageId;
|
||||
if (!packageId && item.packageName) {
|
||||
// CheckPart 没有 packageId 字段,需要通过 packageName 查询获取
|
||||
const pkgRes = await listCheckPackage({ packageName: item.packageName });
|
||||
let packages = pkgRes?.data || [];
|
||||
if (!Array.isArray(packages)) {
|
||||
packages = packages.records || packages.data || [];
|
||||
}
|
||||
if (packages.length === 0) {
|
||||
item.packageDetails = [];
|
||||
return;
|
||||
}
|
||||
packageId = packages[0].id;
|
||||
}
|
||||
const res = await request({
|
||||
url: `/exam/package/${item.packageId}/details`,
|
||||
url: `/system/package/${packageId}/details`,
|
||||
method: 'get'
|
||||
});
|
||||
if (res.code === 200 && res.data) {
|
||||
@@ -460,7 +511,7 @@ async function loadPackageDetailsForItem(item) {
|
||||
...detail,
|
||||
name: detail.name || detail.itemName,
|
||||
unit: detail.unit || '次',
|
||||
price: detail.price || detail.itemPrice || 0,
|
||||
price: detail.price || detail.unitPrice || 0,
|
||||
quantity: detail.quantity || 1
|
||||
}));
|
||||
} else {
|
||||
@@ -522,6 +573,7 @@ const categoryList = ref([]); // 原始分类+项目数据
|
||||
const dictSearchKey = ref('');
|
||||
const activeNames = ref(''); // 当前展开的折叠项
|
||||
const categoryLoadingSet = ref(new Set()); // Bug #500: 正在加载方法的分类集合
|
||||
const isAnimating = ref(false); // Bug #500: 防止快速切换时折叠动画重叠导致抖动
|
||||
|
||||
const allMethods = ref([]);
|
||||
|
||||
@@ -659,6 +711,7 @@ async function handleCategoryExpand(cat) {
|
||||
code: m.code,
|
||||
price: m.price || 0,
|
||||
packageName: m.packageName || '',
|
||||
packageId: m.packageId || null,
|
||||
packagePrice: m.packagePrice || null,
|
||||
serviceFee: m.serviceFee || null
|
||||
}));
|
||||
@@ -669,10 +722,17 @@ async function handleCategoryExpand(cat) {
|
||||
categoryLoadingSet.value.delete(cat.typeId);
|
||||
}
|
||||
}
|
||||
// Bug #500: 改为非 async 函数,避免 el-collapse 的 @change 等待异步完成导致抖动
|
||||
// Bug #500: 添加防抖逻辑,快速切换时跳过中间状态的动画,避免高度跳变和白屏闪烁
|
||||
function handleCollapseChange(activeName) {
|
||||
if (isAnimating.value) return; // 动画进行中,忽略后续点击
|
||||
|
||||
isAnimating.value = true;
|
||||
setTimeout(() => { isAnimating.value = false; }, 300); // 与 CSS 过渡时长一致
|
||||
|
||||
if (activeName) {
|
||||
const cat = filteredCategoryList.value.find(c => c.typeId == activeName);
|
||||
// Bug #428修复: 直接从 categoryList(原始响应式数组)查找分类,
|
||||
// 确保后续 handleCategoryExpand 对 cat.methods 的赋值能正确触发 Vue 响应式更新
|
||||
const cat = categoryList.value.find(c => c.typeId == activeName);
|
||||
if (cat && (!cat.methods || cat.methods.length === 0)) {
|
||||
handleCategoryExpand(cat); // 异步加载,不 await
|
||||
}
|
||||
@@ -912,6 +972,13 @@ function handleSave() {
|
||||
ElMessage.warning('请至少选择一个检查明细项目');
|
||||
return;
|
||||
}
|
||||
// 检查每个项目是否已选择检查方法
|
||||
const itemsWithoutMethod = selectedItems.value.filter(item => !item.selectedMethod);
|
||||
if (itemsWithoutMethod.length > 0) {
|
||||
const names = itemsWithoutMethod.map(item => item.name).join('、');
|
||||
ElMessage.warning(`以下项目未选择检查方法:${names},请在右侧勾选后再保存`);
|
||||
return;
|
||||
}
|
||||
// 从已选项目推导检查类型编码(取第一个项目的 checkType,如 CT / ECG / GI)
|
||||
const firstCheckType = selectedItems.value[0]?.checkType || 'unknown';
|
||||
form.examTypeCode = firstCheckType;
|
||||
@@ -957,12 +1024,20 @@ function handleRowClick(row) {
|
||||
selectedItems.value = [];
|
||||
activeDetailTab.value = 'applyForm';
|
||||
request({ url: `/exam/apply/${row.applyNo}`, method: 'get' }).then(async res => {
|
||||
const d = res.data || res;
|
||||
if (d.data) Object.assign(form, d.data);
|
||||
if (d.items && Array.isArray(d.items)) {
|
||||
const resp = res.data || res;
|
||||
// Bug #408修复: items 在 AjaxResult 顶层(res.items / resp.items),不在 ExamApply 对象内
|
||||
// 防御性提取:优先取顶层 items,兼容嵌套在 resp.data.items 的情况
|
||||
let rawItems = res.items || resp.items;
|
||||
if (!rawItems && resp.data && typeof resp.data === 'object') {
|
||||
rawItems = resp.data.items;
|
||||
}
|
||||
rawItems = rawItems || [];
|
||||
const d = resp.data || resp;
|
||||
if (d) Object.assign(form, d);
|
||||
if (Array.isArray(rawItems) && rawItems.length > 0) {
|
||||
try {
|
||||
// 为每个项目加载检查方法
|
||||
const itemsWithMethods = await Promise.all(d.items.map(async m => {
|
||||
const itemsWithMethods = await Promise.all(rawItems.map(async m => {
|
||||
const item = {
|
||||
id: m.itemCode, name: m.itemName,
|
||||
price: m.itemFee || 0, quantity: 1,
|
||||
@@ -990,12 +1065,18 @@ function handleRowClick(row) {
|
||||
code: md.code,
|
||||
price: m.itemFee || 0, // fallback 到已保存的价格
|
||||
packageName: md.packageName || '',
|
||||
packageId: md.packageId || null,
|
||||
packagePrice: md.packagePrice || null, // Bug #384修复: 套餐价格
|
||||
serviceFee: md.serviceFee || null
|
||||
}));
|
||||
// 如果有已保存的检查方法信息,尝试匹配
|
||||
if (m.checkMethodId) {
|
||||
item.selectedMethod = item.methods.find(md => md.id === m.checkMethodId) || null;
|
||||
// 从已保存的方法中获取套餐信息
|
||||
if (item.selectedMethod?.packageId) {
|
||||
item.isPackage = true;
|
||||
item.packageId = item.selectedMethod.packageId;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -1034,6 +1115,93 @@ function handleDelete(row) {
|
||||
});
|
||||
}
|
||||
|
||||
// Bug #428修复: 判断某个检查方法是否已被选中(任意项目关联了该方法)
|
||||
function isMethodSelected(method, cat) {
|
||||
return selectedItems.value.some(item =>
|
||||
item.selectedMethod?.id === method.id && item.checkType === cat.typeName
|
||||
);
|
||||
}
|
||||
|
||||
// Bug #428修复: 勾选检查方法
|
||||
async function handleMethodSelect(checked, method, cat) {
|
||||
if (checked) {
|
||||
// 找到该方法所属的第一个检查项目
|
||||
const targetItem = cat.items[0];
|
||||
if (!targetItem) {
|
||||
// 如果分类下没有项目,尝试从其他分类找同名项目或创建
|
||||
console.warn('分类下没有检查项目,无法关联方法');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果该项目已存在,只更新 selectedMethod
|
||||
const existingItem = selectedItems.value.find(s => s.id === targetItem.id);
|
||||
if (existingItem) {
|
||||
existingItem.selectedMethod = method;
|
||||
// 从方法中获取套餐信息
|
||||
if (method.packageId) {
|
||||
existingItem.isPackage = true;
|
||||
existingItem.packageId = method.packageId;
|
||||
// 预加载套餐明细
|
||||
loadPackageDetailsForItem(existingItem);
|
||||
}
|
||||
updateMethodDisplay();
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果该项目不存在,创建一个并关联方法
|
||||
if (selectedItems.value.length > 0) {
|
||||
const currentCategory = selectedItems.value[0].checkType;
|
||||
// Bug #428修复: 使用 cat.typeName 进行比较(与 newItem.checkType 保持一致)
|
||||
const newCategory = cat.typeName || '';
|
||||
if (currentCategory !== newCategory) {
|
||||
ElMessage.warning('一个检查单不能同时选择多个项目类型的检查项目');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const newItem = {
|
||||
id: targetItem.id, name: targetItem.name,
|
||||
price: targetItem.price, quantity: 1,
|
||||
serviceFee: targetItem.serviceFee || 0,
|
||||
unit: targetItem.unit || '次',
|
||||
applyPart: targetItem.name,
|
||||
checkType: cat.typeName,
|
||||
nationalCode: targetItem.nationalCode || '',
|
||||
checked: true,
|
||||
methods: [method],
|
||||
selectedMethod: method,
|
||||
expanded: false,
|
||||
// 从方法中获取套餐信息(优先级高于项目本身的 packageName)
|
||||
isPackage: !!method.packageId || !!targetItem.packageName,
|
||||
packageId: method.packageId || targetItem.packageId || null
|
||||
};
|
||||
selectedItems.value.push(newItem);
|
||||
|
||||
// 如果是套餐,预加载套餐明细
|
||||
if (newItem.isPackage && newItem.packageId) {
|
||||
loadPackageDetailsForItem(newItem);
|
||||
}
|
||||
|
||||
// 自动回填执行科室
|
||||
if (selectedItems.value.length === 1 && cat?.performDeptName) {
|
||||
form.performDeptCode = cat.performDeptName;
|
||||
}
|
||||
|
||||
// 同时勾选左侧项目的 checkbox
|
||||
targetItem.checked = true;
|
||||
|
||||
} else {
|
||||
// 取消选择方法:将 selectedItems 中关联该方法的项的 selectedMethod 清空
|
||||
const itemsWithMethod = selectedItems.value.filter(
|
||||
item => item.selectedMethod?.id === method.id
|
||||
);
|
||||
for (const item of itemsWithMethod) {
|
||||
item.selectedMethod = null;
|
||||
}
|
||||
}
|
||||
updateMethodDisplay();
|
||||
}
|
||||
|
||||
// ====== 勾选逻辑 ======
|
||||
async function handleItemSelect(checked, item, cat) {
|
||||
if (checked) {
|
||||
@@ -1057,6 +1225,7 @@ async function handleItemSelect(checked, item, cat) {
|
||||
code: m.code,
|
||||
price: m.price || item.price, // fallback 到项目价格
|
||||
packageName: m.packageName || '',
|
||||
packageId: m.packageId || null,
|
||||
packagePrice: m.packagePrice || null, // Bug #384修复: 套餐价格
|
||||
serviceFee: m.serviceFee || null // Bug #384修复: 服务费
|
||||
}));
|
||||
@@ -1068,7 +1237,8 @@ async function handleItemSelect(checked, item, cat) {
|
||||
|
||||
if (selectedItems.value.length > 0) {
|
||||
const currentCategory = selectedItems.value[0].checkType;
|
||||
const newCategory = cat.typeCode || '';
|
||||
// Bug #428修复: 使用 cat.typeName 进行比较(与 effectiveCheckType 保持一致)
|
||||
const newCategory = cat.typeName || '';
|
||||
if (currentCategory !== newCategory) {
|
||||
ElMessage.warning('一个检查单不能同时选择多个项目类型的检查项目');
|
||||
item.checked = false;
|
||||
@@ -1089,6 +1259,7 @@ async function handleItemSelect(checked, item, cat) {
|
||||
selectedMethod: null,
|
||||
expanded: false, // Bug #384修复: 新增展开状态,默认不展开
|
||||
isPackage: !!item.packageName, // Bug #428修复: 标记是否为套餐
|
||||
packageName: item.packageName || null, // Bug #426修复: 套餐名称,用于查找packageId
|
||||
packageId: item.packageId || null // Bug #428修复: 套餐ID
|
||||
});
|
||||
|
||||
@@ -1099,12 +1270,6 @@ async function handleItemSelect(checked, item, cat) {
|
||||
form.performDeptCode = cat.performDeptName;
|
||||
}
|
||||
|
||||
// 如果有且仅有一个检查方法,自动选中并更新显示
|
||||
if (methods.length === 1) {
|
||||
const lastIdx = selectedItems.value.length - 1;
|
||||
selectedItems.value[lastIdx].selectedMethod = methods[0];
|
||||
updateMethodDisplay(); // Bug #384修复: 联动更新显示
|
||||
}
|
||||
} else {
|
||||
const idx = selectedItems.value.findIndex(s => s.id === item.id);
|
||||
if (idx > -1) selectedItems.value.splice(idx, 1);
|
||||
@@ -1127,21 +1292,68 @@ async function toggleItemExpand(item) {
|
||||
}
|
||||
|
||||
// Bug #384修复: 勾选框选择检查方法(单选逻辑)
|
||||
function selectMethodCheckbox(checked, item, method) {
|
||||
async function selectMethodCheckbox(checked, item, method) {
|
||||
if (checked) {
|
||||
item.selectedMethod = method;
|
||||
// 动态加载该方法对应的套餐明细
|
||||
await loadMethodPackageDetails(item, method);
|
||||
} else {
|
||||
item.selectedMethod = null;
|
||||
item.methodPackageDetails = [];
|
||||
}
|
||||
// 联动更新表单检查方法显示字段
|
||||
updateMethodDisplay();
|
||||
|
||||
|
||||
// #430: 套餐金额实时同步到申请单
|
||||
nextTick(() => {
|
||||
form.totalAmount = totalAmountCalc.value;
|
||||
});
|
||||
}
|
||||
|
||||
// 根据检查方法的packageName加载对应的套餐明细
|
||||
async function loadMethodPackageDetails(item, method) {
|
||||
item.methodPackageLoading = true;
|
||||
item.methodPackageDetails = [];
|
||||
try {
|
||||
if (!method.packageName) {
|
||||
item.methodPackageLoading = false;
|
||||
return;
|
||||
}
|
||||
// 通过packageName查询套餐获取packageId
|
||||
const pkgRes = await listCheckPackage({ packageName: method.packageName });
|
||||
let packages = pkgRes?.data || [];
|
||||
if (!Array.isArray(packages)) {
|
||||
packages = packages.records || packages.data || [];
|
||||
}
|
||||
if (packages.length === 0) {
|
||||
item.methodPackageLoading = false;
|
||||
return;
|
||||
}
|
||||
const packageId = packages[0].id;
|
||||
// 查询套餐明细
|
||||
const detailRes = await request({
|
||||
url: `/system/package/${packageId}/details`,
|
||||
method: 'get'
|
||||
});
|
||||
if (detailRes.code === 200 && detailRes.data) {
|
||||
item.methodPackageDetails = detailRes.data.map(d => ({
|
||||
id: d.id,
|
||||
name: d.itemName || d.name,
|
||||
quantity: d.quantity || 1,
|
||||
unit: d.unit || '次',
|
||||
price: d.unitPrice || d.price || 0,
|
||||
amount: d.amount || d.total || 0,
|
||||
checked: true // 默认勾选
|
||||
}));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载方法套餐明细失败:', err);
|
||||
item.methodPackageDetails = [];
|
||||
} finally {
|
||||
item.methodPackageLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Bug #384修复: 更新检查方法显示字段(联动)
|
||||
function updateMethodDisplay() {
|
||||
// 找到第一个有选中检查方法的项目
|
||||
@@ -1307,6 +1519,7 @@ defineExpose({ getList });
|
||||
.collapse-scroll {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden; /* Bug #500: 防止切换时水平方向溢出导致抖动 */
|
||||
}
|
||||
.empty-hint {
|
||||
color: #909399;
|
||||
@@ -1359,6 +1572,53 @@ defineExpose({ getList });
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
/* Bug #428修复: 分类下检查方法区域样式 */
|
||||
.method-section {
|
||||
padding: 6px 8px;
|
||||
background: #f0f7ff;
|
||||
border-radius: 4px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.method-section-title {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
margin-bottom: 4px;
|
||||
padding-bottom: 3px;
|
||||
border-bottom: 1px dashed #d9ecff;
|
||||
}
|
||||
|
||||
.method-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 3px 4px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.method-row:hover {
|
||||
background: #e8f4ff;
|
||||
}
|
||||
|
||||
.method-checkbox {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.method-checkbox :deep(.el-checkbox__label) {
|
||||
font-size: 12px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.method-price-tag {
|
||||
font-size: 11px;
|
||||
color: #e6a23c;
|
||||
font-weight: 500;
|
||||
flex-shrink: 0;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
/* 已选择 tags */
|
||||
.selected-panel {
|
||||
width: 140px; /* Bug #384修复: 加宽以适应展开内容 */
|
||||
@@ -1430,6 +1690,41 @@ defineExpose({ getList });
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
/* Bug #428修复: 套餐明细列表样式 */
|
||||
.package-details-list {
|
||||
padding: 6px 10px;
|
||||
background: #fffbe6;
|
||||
border-top: 1px solid #ffe58f;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.detail-row:hover {
|
||||
background: #fff9e6;
|
||||
}
|
||||
|
||||
.detail-name {
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.detail-info {
|
||||
color: #909399;
|
||||
font-size: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Bug #384修复: 检查方法勾选框列表 */
|
||||
.method-list {
|
||||
padding: 6px 10px;
|
||||
@@ -1464,6 +1759,29 @@ defineExpose({ getList });
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
/* 选中方法后显示的套餐明细 */
|
||||
.method-package-details {
|
||||
margin-top: 4px;
|
||||
padding: 4px 0;
|
||||
border-top: 1px dashed #dcdfe6;
|
||||
}
|
||||
|
||||
.method-package-header {
|
||||
padding: 2px 0 4px 24px;
|
||||
}
|
||||
|
||||
.method-package-title {
|
||||
font-size: 10px;
|
||||
color: #909399;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.method-package-loading {
|
||||
padding: 4px 0 4px 24px;
|
||||
font-size: 10px;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
/* 折叠组件细节 */
|
||||
:deep(.el-collapse) {
|
||||
border: none;
|
||||
@@ -1479,10 +1797,10 @@ defineExpose({ getList });
|
||||
padding-bottom: 4px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
/* Bug #500: 折叠面板展开/收起动画使用 will-change 优化性能 */
|
||||
/* Bug #500: 折叠面板动画容器,添加 overflow:hidden 防止展开时内容溢出导致闪烁 */
|
||||
:deep(.el-collapse-item__wrap) {
|
||||
border: none;
|
||||
will-change: height;
|
||||
overflow: hidden;
|
||||
}
|
||||
:deep(.el-collapse-item) {
|
||||
transition: margin 0.2s ease;
|
||||
|
||||
@@ -109,6 +109,7 @@ const props = defineProps({
|
||||
});
|
||||
const { proxy } = getCurrentInstance();
|
||||
const encounterId = ref();
|
||||
const firstEnum = ref(1); // 初复诊标识:1=初诊,2=复诊
|
||||
onMounted(() => {
|
||||
getPatientList();
|
||||
});
|
||||
@@ -127,6 +128,7 @@ function getPatientList() {
|
||||
|
||||
function clickRow(row) {
|
||||
encounterId.value = row.encounterId;
|
||||
firstEnum.value = row.firstEnum ?? row.first_enum ?? 1;
|
||||
emits('cellClick', row);
|
||||
}
|
||||
|
||||
@@ -182,7 +184,7 @@ function handleComplete() {
|
||||
}
|
||||
proxy.$modal.confirm('是否完成该患者问诊?').then(() => {
|
||||
proxy.$modal.loading();
|
||||
completeEncounter(encounterId.value).then(() => {
|
||||
completeEncounter({ encounterId: encounterId.value, firstEnum: firstEnum.value }).then(() => {
|
||||
proxy.$modal.closeLoading();
|
||||
proxy.$modal.msgSuccess('完成问诊成功');
|
||||
getPatientList();
|
||||
|
||||
@@ -246,7 +246,8 @@
|
||||
<el-input-number :min="0" v-model="scope.row.doseQuantity" controls-position="right"
|
||||
:controls="false" style="width: 70px; margin-right: 20px"
|
||||
: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-select v-model="scope.row.minUnitCode" style="width: 70px; margin-right: 20px" placeholder=" ">
|
||||
@@ -294,7 +295,8 @@
|
||||
}
|
||||
// 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)"
|
||||
:key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
@@ -313,6 +315,7 @@
|
||||
data-prop="dispensePerDuration">
|
||||
<el-input-number v-model="scope.row.dispensePerDuration" style="width: 80px" :min="1"
|
||||
controls-position="right" :controls="false" :ref="(el) => (inputRefs.dispensePerDuration = el)"
|
||||
@change="calculateTotalAmount(scope.row, scope.$index)"
|
||||
@keyup.enter.prevent="
|
||||
handleEnter('dispensePerDuration', scope.row, scope.$index)
|
||||
">
|
||||
@@ -1010,15 +1013,29 @@ const mapAdviceTypeLabel = (type, adviceTableName) => {
|
||||
if (type === 2 && adviceTableName === 'adm_device_definition') {
|
||||
return '耗材';
|
||||
}
|
||||
|
||||
|
||||
// 🔧 Bug Fix: 处理检查类型(adviceType=23)
|
||||
// 检查类型属于诊疗类,应该显示为"检查"
|
||||
if (type === 23) {
|
||||
return '检查';
|
||||
}
|
||||
|
||||
|
||||
const found = adviceTypeList.value.find((item) => item.value === type);
|
||||
return found ? found.label : '';
|
||||
if (found) {
|
||||
return found.label;
|
||||
}
|
||||
|
||||
// 🔧 Bug #458 Fix: 诊疗/手术类型字典缺失时的兜底,避免保存后"医嘱类型"列显示为空
|
||||
if (adviceTableName === 'wor_activity_definition' || adviceTableName === 'wor_service_request') {
|
||||
if (type === 6) return '手术';
|
||||
if (type === 4) return '手术';
|
||||
if (type === 1) return '检验';
|
||||
if (type === 2) return '检查';
|
||||
if (type === 5) return '其他';
|
||||
return '诊疗';
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
// 西药处方管理相关变量
|
||||
@@ -2492,11 +2509,13 @@ function handleSave(prescriptionId) {
|
||||
|
||||
// 🔧 BugFix#318: 从 parsedContent 提取标准医嘱字段,排除手术特有字段
|
||||
const standardFields = [
|
||||
'accountId', 'chargeItemId', 'conditionDefinitionId', 'conditionId',
|
||||
'contentJson', 'definitionDetailId', 'definitionId', 'diagnosisName',
|
||||
'dosageInstruction', 'effectiveOrgId', 'encounterDiagnosisId',
|
||||
'encounterId', 'lotNumber', 'patientId', 'practitionerId',
|
||||
'prescriptionNo', 'skinTestFlag', 'unitPrice', 'volume', 'ybClassEnum'
|
||||
'accountId', 'chargeItemId', 'conditionDefinitionId', 'conditionId',
|
||||
'contentJson', 'definitionDetailId', 'definitionId', 'diagnosisName',
|
||||
'dosageInstruction', 'effectiveOrgId', 'encounterDiagnosisId',
|
||||
'encounterId', 'lotNumber', 'patientId', 'practitionerId',
|
||||
'prescriptionNo', 'skinTestFlag', 'unitPrice', 'volume', 'ybClassEnum',
|
||||
// 🔧 Bug Fix: 添加 therapyEnum 字段(医嘱类型:1=长期, 2=临时)
|
||||
'therapyEnum'
|
||||
];
|
||||
let filteredContent = {};
|
||||
standardFields.forEach(field => {
|
||||
@@ -3140,7 +3159,9 @@ function handleSaveBatch(prescriptionId) {
|
||||
// 🔧 Bug Fix: 添加 definitionId 和 definitionDetailId 字段
|
||||
'definitionId', 'definitionDetailId',
|
||||
// 🔧 Bug Fix: 添加 categoryEnum 字段(耗材必填)
|
||||
'categoryEnum'
|
||||
'categoryEnum',
|
||||
// 🔧 Bug Fix: 添加 therapyEnum 字段(医嘱类型:1=长期, 2=临时)
|
||||
'therapyEnum'
|
||||
];
|
||||
let filteredItem = {};
|
||||
standardItemFields.forEach(field => {
|
||||
@@ -3305,9 +3326,13 @@ function syncGroupFields(row) {
|
||||
}
|
||||
|
||||
// 同步执行科室
|
||||
// 🔧 Bug #455: 诊疗类医嘱(adviceType=3)不使用项目配置的执行科室,
|
||||
// 避免配置ID不在机构树中导致显示原始ID,保持患者就诊科室即可
|
||||
if (row.orgId || row.positionId) {
|
||||
// 🔧 修复:优先使用项目所属科室(orgId),其次positionId
|
||||
prescriptionList.value[rowIndex.value].orgId = row.orgId || row.positionId;
|
||||
if (Number(row.adviceType) != 3) {
|
||||
// 🔧 修复:优先使用项目所属科室(orgId),其次positionId
|
||||
prescriptionList.value[rowIndex.value].orgId = row.orgId || row.positionId;
|
||||
}
|
||||
}
|
||||
|
||||
// 同步皮试标记
|
||||
@@ -3339,7 +3364,7 @@ function syncGroupFields(row) {
|
||||
}
|
||||
}
|
||||
|
||||
function setValue(row) {
|
||||
async function setValue(row) {
|
||||
unitCodeList.value = [];
|
||||
unitCodeList.value.push({ value: row.unitCode, label: row.unitCode_dictText, type: 'unit' });
|
||||
|
||||
@@ -3391,8 +3416,11 @@ function setValue(row) {
|
||||
showPopover: false, // 确保查询框关闭
|
||||
};
|
||||
console.log('[BugFix] setValue - prescriptionList[rowIndex].adviceType_dictText:', prescriptionList.value[rowIndex.value].adviceType_dictText);
|
||||
// 🔧 修复执行科室逻辑:优先使用项目维护的所属科室(row.orgId),其次使用positionId,最后回退到患者科室
|
||||
prescriptionList.value[rowIndex.value].orgId = row.orgId || row.positionId || props.patientInfo?.orgId;
|
||||
// 🔧 Bug #455: 诊疗医嘱(adviceType=3)的执行科室默认使用患者就诊科室,
|
||||
// 不使用positionId(诊疗目录配置的执行科室),避免配置ID不在机构树中导致显示原始ID
|
||||
if (Number(row.adviceType) != 3) {
|
||||
prescriptionList.value[rowIndex.value].orgId = row.positionId || props.patientInfo?.orgId;
|
||||
}
|
||||
prescriptionList.value[rowIndex.value].dose = row.dose || row.doseQuantity;
|
||||
prescriptionList.value[rowIndex.value].quantity = row.quantity || 1;
|
||||
prescriptionList.value[rowIndex.value].unitCodeList = unitCodeList.value;
|
||||
@@ -3511,7 +3539,7 @@ function setValue(row) {
|
||||
prescriptionList.value[rowIndex.value].positionId = finalLocationId;
|
||||
}
|
||||
} else {
|
||||
getOrgList();
|
||||
await getOrgList();
|
||||
// 会诊类型(adviceType == 5)和诊疗类型(adviceType == 3)的处理
|
||||
if (row.adviceType == 5) {
|
||||
// 会诊类型:设置默认值
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
|
||||
|
||||
<!-- 手术单号 -->
|
||||
<el-table-column label="手术单号" align="center" width="150">
|
||||
<template #default="scope">
|
||||
@@ -33,160 +33,31 @@
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<!-- 申请日期 -->
|
||||
<el-table-column label="申请日期" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
{{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}') }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<!-- 患者姓名 -->
|
||||
<el-table-column label="患者姓名" align="center" prop="patientName" width="100" />
|
||||
|
||||
|
||||
<!-- 申请医生 -->
|
||||
<el-table-column label="申请医生" align="center" prop="applyDoctorName" width="100" />
|
||||
|
||||
|
||||
<!-- 申请科室 -->
|
||||
<el-table-column label="申请科室" align="center" prop="applyDeptName" width="120" show-overflow-tooltip />
|
||||
|
||||
|
||||
<!-- 手术名称 -->
|
||||
<el-table-column label="手术名称" align="center" prop="surgeryName" min-width="150" show-overflow-tooltip />
|
||||
|
||||
|
||||
<!-- 手术等级 -->
|
||||
<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">
|
||||
<template #default="scope">
|
||||
{{ scope.row.operatingRoomConfirmTime ? parseTime(scope.row.operatingRoomConfirmTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
||||
@@ -204,16 +75,16 @@ defineExpose({
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<!-- 操作 -->
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right">
|
||||
<template #default="scope">
|
||||
<!-- 查看:显示手术申请详情(只读模式) -->
|
||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)">查看</el-button>
|
||||
|
||||
|
||||
<!-- 编辑:修改手术申请信息(只有状态为新开的能修改) -->
|
||||
<el-button link type="primary" icon="Edit" @click="handleEdit(scope.row)" v-if="scope.row.statusEnum === 0">编辑</el-button>
|
||||
|
||||
|
||||
<!-- 删除:取消手术申请(作废) -->
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-if="scope.row.statusEnum === 0 || scope.row.statusEnum === 1">删除</el-button>
|
||||
</template>
|
||||
@@ -640,6 +511,10 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const loading = ref(true)
|
||||
const surgeryLoading = ref(false)
|
||||
const anesthesiaLoading = ref(false)
|
||||
let surgerySearchTimer = null
|
||||
let anesthesiaSearchTimer = null
|
||||
const surgeryList = ref([])
|
||||
const open = ref(false)
|
||||
const viewOpen = ref(false)
|
||||
@@ -1258,12 +1133,13 @@ function submitForm() {
|
||||
// 新增手术
|
||||
addSurgery(form.value).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess(res.msg || '新增成功')
|
||||
proxy.$modal.msgSuccess(res.msg || '手术申请提交成功!')
|
||||
// 保存麻醉方式
|
||||
sessionStorage.setItem('anesthesiaType', form.value.anesthesiaTypeEnum)
|
||||
open.value = false
|
||||
emit('saved') // 通知父组件刷新医嘱列表
|
||||
// 刷新手术申请列表
|
||||
getList()
|
||||
emit('saved') // 🔧 触发保存事件,通知父组件刷新医嘱列表
|
||||
} else {
|
||||
proxy.$modal.msgError(res.msg || '新增手术失败,请检查表单信息')
|
||||
}
|
||||
@@ -1275,12 +1151,13 @@ function submitForm() {
|
||||
// 修改手术
|
||||
updateSurgery(form.value).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess(res.msg || '修改成功')
|
||||
proxy.$modal.msgSuccess(res.msg || '手术申请修改成功!')
|
||||
// 保存麻醉方式
|
||||
sessionStorage.setItem('anesthesiaType', form.value.anesthesiaTypeEnum)
|
||||
open.value = false
|
||||
emit('saved') // 通知父组件刷新医嘱列表
|
||||
// 刷新手术申请列表
|
||||
getList()
|
||||
emit('saved') // 🔧 触发保存事件,通知父组件刷新医嘱列表
|
||||
} else {
|
||||
proxy.$modal.msgError(res.msg || '更新手术失败,请检查表单信息')
|
||||
}
|
||||
|
||||
@@ -151,23 +151,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
v-model="detailVisible"
|
||||
:title="detailMode === 'view' ? '报卡详情 - 中华人民共和国传染病报告卡' : '编辑报卡 - 中华人民共和国传染病报告卡'"
|
||||
width="1100px"
|
||||
destroy-on-close
|
||||
class="card-detail-dialog"
|
||||
>
|
||||
<InfectiousReport
|
||||
:mode=" detailMode"
|
||||
:card-data="currentCard"
|
||||
@submit-edit="handleSaveEdit"
|
||||
style="max-height: 75vh; overflow-y: auto;"
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button @click="detailVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<InfectiousDiseaseReportDialog
|
||||
ref="infectiousDiseaseReportRef"
|
||||
:read-only="detailMode === 'view'"
|
||||
@success="detailVisible = false"
|
||||
@close="detailVisible = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -175,7 +164,7 @@
|
||||
import { ref, reactive, onMounted, onActivated, onBeforeUnmount, nextTick, computed } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { DataAnalysis, Warning, CircleCheck, Check, RefreshRight } from '@element-plus/icons-vue';
|
||||
import InfectiousReport from '../components/infectiousReport/index.vue';
|
||||
import InfectiousDiseaseReportDialog from '../components/diagnosis/infectiousDiseaseReportDialog.vue';
|
||||
import {
|
||||
getDoctorCardStatistics,
|
||||
getDoctorCardList,
|
||||
@@ -185,7 +174,6 @@ import {
|
||||
batchDeleteCards,
|
||||
exportCardToWord,
|
||||
getCardDetail,
|
||||
updateDoctorCard,
|
||||
} from './api';
|
||||
|
||||
const loading = ref(false);
|
||||
@@ -211,7 +199,7 @@ const queryParams = reactive({
|
||||
|
||||
const detailVisible = ref(false);
|
||||
const detailMode = ref('view');
|
||||
const currentCard = ref({});
|
||||
const infectiousDiseaseReportRef = ref(null);
|
||||
|
||||
// 计算表格高度:根据视口高度动态调整
|
||||
const tableHeight = computed(() => {
|
||||
@@ -327,9 +315,11 @@ async function handleView(row) {
|
||||
try {
|
||||
const res = await getCardDetail(row.cardNo);
|
||||
if (res.code === 200) {
|
||||
currentCard.value = res.data || {};
|
||||
detailMode.value = 'view';
|
||||
detailVisible.value = true;
|
||||
nextTick(() => {
|
||||
infectiousDiseaseReportRef.value?.showReport(res.data || {});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('获取详情失败');
|
||||
@@ -340,57 +330,17 @@ async function handleEdit(row) {
|
||||
try {
|
||||
const res = await getCardDetail(row.cardNo);
|
||||
if (res.code === 200) {
|
||||
currentCard.value = res.data || {};
|
||||
detailMode.value = 'edit';
|
||||
detailVisible.value = true;
|
||||
nextTick(() => {
|
||||
infectiousDiseaseReportRef.value?.showReport(res.data || {}, false);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('获取详情失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSaveEdit(submitData) {
|
||||
// submitData 来自 InfectiousReport 组件的 submit-edit 事件
|
||||
try {
|
||||
const updateData = {
|
||||
cardNo: submitData.cardNo,
|
||||
phone: submitData.phone,
|
||||
contactPhone: submitData.contactPhone,
|
||||
onsetDate: submitData.onsetDate,
|
||||
diagDate: submitData.diagDate,
|
||||
diseaseCode: submitData.diseaseCode,
|
||||
diseaseType: submitData.diseaseType,
|
||||
otherDisease: submitData.otherDisease,
|
||||
caseClass: submitData.caseClass,
|
||||
occupation: submitData.occupation,
|
||||
patientBelong: submitData.patientBelong,
|
||||
addressProv: submitData.addressProv,
|
||||
addressCity: submitData.addressCity,
|
||||
addressCounty: submitData.addressCounty,
|
||||
addressTown: submitData.addressTown,
|
||||
addressVillage: submitData.addressVillage,
|
||||
addressHouse: submitData.addressHouse,
|
||||
workplace: submitData.workplace,
|
||||
parentName: submitData.parentName,
|
||||
deathDate: submitData.deathDate,
|
||||
correctName: submitData.correctName,
|
||||
withdrawReason: submitData.withdrawReason,
|
||||
remark: submitData.remark,
|
||||
};
|
||||
|
||||
const res = await updateDoctorCard(updateData);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('保存成功');
|
||||
detailVisible.value = false;
|
||||
getList();
|
||||
} else {
|
||||
ElMessage.error(res.msg || '保存失败');
|
||||
}
|
||||
} catch (error) {
|
||||
ElMessage.error('保存失败:' + (error.message || '网络错误'));
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSubmit(row) {
|
||||
try {
|
||||
await ElMessageBox.confirm('确认提交该报卡?', '提示', {
|
||||
@@ -800,17 +750,4 @@ function handleResize() {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 报卡详情弹窗 */
|
||||
:deep(.card-detail-dialog .el-dialog__body) {
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:deep(.card-detail-dialog .infectious-report-container) {
|
||||
padding: 16px;
|
||||
height: auto;
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -44,6 +44,17 @@ export function getSurgery(queryParams) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页查询手术申请单(全局,不需要encounterId,用于门诊手术安排查找弹窗)
|
||||
*/
|
||||
export function getSurgeryPage(queryParams) {
|
||||
return request({
|
||||
url: '/reg-doctorstation/request-form/get-surgery-page',
|
||||
method: 'get',
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询护理医嘱信息
|
||||
*/
|
||||
|
||||
@@ -175,10 +175,9 @@ const hasMatchedFields = computed(() => {
|
||||
});
|
||||
|
||||
/** 查询科室 */
|
||||
const getLocationInfo = () => {
|
||||
getOrgList().then((res) => {
|
||||
orgOptions.value = res.data.records;
|
||||
});
|
||||
const getLocationInfo = async () => {
|
||||
const res = await getOrgList();
|
||||
orgOptions.value = res.data.records;
|
||||
};
|
||||
|
||||
const recursionFun = (targetDepartment) => {
|
||||
@@ -199,7 +198,12 @@ const recursionFun = (targetDepartment) => {
|
||||
return name;
|
||||
};
|
||||
|
||||
const handleViewDetail = (row) => {
|
||||
const handleViewDetail = async (row) => {
|
||||
// 确保科室数据已加载,以便将 ID 解析为名称
|
||||
if (!orgOptions.value || orgOptions.value.length === 0) {
|
||||
await getLocationInfo();
|
||||
}
|
||||
|
||||
currentDetail.value = row;
|
||||
// 解析 descJson
|
||||
if (row.descJson) {
|
||||
@@ -220,10 +224,9 @@ const handleViewDetail = (row) => {
|
||||
|
||||
watch(
|
||||
() => patientInfo.value?.encounterId,
|
||||
(val) => {
|
||||
async (val) => {
|
||||
if (val) {
|
||||
fetchData();
|
||||
getLocationInfo();
|
||||
await Promise.all([fetchData(), getLocationInfo()]);
|
||||
} else {
|
||||
tableData.value = [];
|
||||
}
|
||||
|
||||
@@ -49,15 +49,6 @@
|
||||
<el-option label="已作废" value="7" />
|
||||
</el-select>
|
||||
</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-button type="primary" @click="handleSearch" :loading="loading">
|
||||
<el-icon><Search /></el-icon>
|
||||
@@ -88,56 +79,16 @@
|
||||
<el-table-column prop="patientName" label="患者姓名" width="120" />
|
||||
<el-table-column prop="name" label="申请单名称" width="140" />
|
||||
<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 label="申请单状态" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<span>{{ parseStatus(scope.row.status) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" width="240">
|
||||
<el-table-column label="操作" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<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>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -167,7 +118,7 @@
|
||||
<el-descriptions-item label="创建时间">{{
|
||||
currentDetail.createTime || '-'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="处方号">{{
|
||||
<el-descriptions-item label="申请单号">{{
|
||||
currentDetail.prescriptionNo || '-'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请者">{{
|
||||
@@ -209,60 +160,6 @@
|
||||
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</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-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>
|
||||
</template>
|
||||
|
||||
@@ -270,8 +167,8 @@
|
||||
import {computed, getCurrentInstance, ref, watch} from 'vue';
|
||||
import {Refresh, Search} from '@element-plus/icons-vue';
|
||||
import {patientInfo} from '../../store/patient.js';
|
||||
import {getCheck, deleteRequestForm, withdrawRequestForm, getTestResult} from './api';
|
||||
import {getOrgList} from '@/views/doctorstation/components/api.js';
|
||||
import {getCheck} from './api';
|
||||
import {getDepartmentList} from '@/api/public.js';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
@@ -282,33 +179,10 @@ const currentDetail = ref(null);
|
||||
const descJsonData = ref(null);
|
||||
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({
|
||||
dateRange: getDefaultDateRange(), // 默认近一周
|
||||
dateRange: [], // [startDate, endDate]
|
||||
status: '', // 申请单状态
|
||||
keyword: '', // 关键字搜索
|
||||
});
|
||||
|
||||
const fetchData = async () => {
|
||||
@@ -333,11 +207,6 @@ const fetchData = async () => {
|
||||
params.status = filterForm.value.status;
|
||||
}
|
||||
|
||||
// 添加关键字搜索
|
||||
if (filterForm.value.keyword && filterForm.value.keyword.trim()) {
|
||||
params.keyword = filterForm.value.keyword.trim();
|
||||
}
|
||||
|
||||
const res = await getCheck(params);
|
||||
if (res.code === 200 && res.data) {
|
||||
const raw = res.data?.records || res.data;
|
||||
@@ -374,9 +243,8 @@ const handleSearch = async () => {
|
||||
* 重置按钮处理
|
||||
*/
|
||||
const handleReset = () => {
|
||||
filterForm.value.dateRange = getDefaultDateRange();
|
||||
filterForm.value.dateRange = [];
|
||||
filterForm.value.status = '';
|
||||
filterForm.value.keyword = '';
|
||||
fetchData();
|
||||
};
|
||||
|
||||
@@ -425,8 +293,8 @@ const hasMatchedFields = computed(() => {
|
||||
|
||||
/** 查询科室 */
|
||||
const getLocationInfo = () => {
|
||||
getOrgList().then((res) => {
|
||||
orgOptions.value = res.data.records;
|
||||
getDepartmentList().then((res) => {
|
||||
orgOptions.value = res.data || [];
|
||||
});
|
||||
};
|
||||
|
||||
@@ -438,17 +306,19 @@ const recursionFun = (targetDepartment) => {
|
||||
name = obj.name;
|
||||
}
|
||||
const subObjArray = obj['children'];
|
||||
for (let index = 0; index < subObjArray.length; index++) {
|
||||
const item = subObjArray[index];
|
||||
if (item.id == targetDepartment) {
|
||||
name = item.name;
|
||||
if (subObjArray && subObjArray.length > 0) {
|
||||
for (let index = 0; index < subObjArray.length; index++) {
|
||||
const item = subObjArray[index];
|
||||
if (item.id == targetDepartment) {
|
||||
name = item.name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
const handleViewDetail = (row) => {
|
||||
const handleViewDetail = async (row) => {
|
||||
console.log('targetDepartment========>', JSON.stringify(row));
|
||||
|
||||
currentDetail.value = row;
|
||||
@@ -456,6 +326,15 @@ const handleViewDetail = (row) => {
|
||||
if (row.descJson) {
|
||||
try {
|
||||
const obj = JSON.parse(row.descJson);
|
||||
// 确保科室数据已加载
|
||||
if (!orgOptions.value || orgOptions.value.length === 0) {
|
||||
await new Promise((resolve) => {
|
||||
getDepartmentList().then((res) => {
|
||||
orgOptions.value = res.data || [];
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
obj.targetDepartment = recursionFun(obj.targetDepartment);
|
||||
descJsonData.value = obj;
|
||||
} catch (e) {
|
||||
@@ -468,217 +347,6 @@ const handleViewDetail = (row) => {
|
||||
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.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({
|
||||
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>${row.name || '-'}</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(
|
||||
() => patientInfo.value?.encounterId,
|
||||
(val) => {
|
||||
@@ -687,9 +355,8 @@ watch(
|
||||
getLocationInfo();
|
||||
} else {
|
||||
tableData.value = [];
|
||||
filterForm.value.dateRange = getDefaultDateRange();
|
||||
filterForm.value.dateRange = [];
|
||||
filterForm.value.status = '';
|
||||
filterForm.value.keyword = '';
|
||||
}
|
||||
},
|
||||
{ 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 = () => {
|
||||
getOrgList().then((res) => {
|
||||
orgOptions.value = res.data.records;
|
||||
});
|
||||
const getLocationInfo = async () => {
|
||||
const res = await getOrgList();
|
||||
orgOptions.value = res.data.records;
|
||||
};
|
||||
|
||||
const recursionFun = (targetDepartment) => {
|
||||
@@ -195,9 +194,8 @@ const recursionFun = (targetDepartment) => {
|
||||
name = obj.name;
|
||||
}
|
||||
const subObjArray = obj['children'];
|
||||
if (!subObjArray) continue;
|
||||
for (let i = 0; i < subObjArray.length; i++) {
|
||||
const item = subObjArray[i];
|
||||
for (let index = 0; index < subObjArray.length; index++) {
|
||||
const item = subObjArray[index];
|
||||
if (item.id == targetDepartment) {
|
||||
name = item.name;
|
||||
}
|
||||
@@ -206,7 +204,12 @@ const recursionFun = (targetDepartment) => {
|
||||
return name;
|
||||
};
|
||||
|
||||
const handleViewDetail = (row) => {
|
||||
const handleViewDetail = async (row) => {
|
||||
// 确保科室数据已加载,以便将 ID 解析为名称
|
||||
if (!orgOptions.value || orgOptions.value.length === 0) {
|
||||
await getLocationInfo();
|
||||
}
|
||||
|
||||
currentDetail.value = row;
|
||||
// 解析 descJson
|
||||
if (row.descJson) {
|
||||
@@ -227,10 +230,9 @@ const handleViewDetail = (row) => {
|
||||
|
||||
watch(
|
||||
() => patientInfo.value?.encounterId,
|
||||
(val) => {
|
||||
async (val) => {
|
||||
if (val) {
|
||||
fetchData();
|
||||
getLocationInfo();
|
||||
await Promise.all([fetchData(), getLocationInfo()]);
|
||||
} else {
|
||||
tableData.value = [];
|
||||
}
|
||||
|
||||
@@ -41,12 +41,19 @@
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="待签发" value="0" />
|
||||
<el-option label="已签发" value="1" />
|
||||
<el-option label="已采集" value="2" />
|
||||
<el-option label="已收样" value="3" />
|
||||
<el-option label="报告已出" value="4" />
|
||||
<el-option label="已作废" value="5" />
|
||||
</el-select>
|
||||
</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-button type="primary" @click="handleSearch" :loading="loading">
|
||||
<el-icon><Search /></el-icon>
|
||||
@@ -94,8 +101,15 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="requesterId_dictText" label="申请者" width="120" />
|
||||
<el-table-column label="操作" align="center" fixed="right">
|
||||
<el-table-column label="操作" align="center" fixed="right" width="160">
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.billStatus == 0 || scope.row.status == 0">
|
||||
<el-button link type="primary" @click="handleEdit(scope.row)">修改</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
<template v-else-if="scope.row.billStatus == 1 || scope.row.status == 1">
|
||||
<el-button link type="warning" @click="handleWithdraw(scope.row)">撤回</el-button>
|
||||
</template>
|
||||
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -172,7 +186,7 @@
|
||||
import {computed, getCurrentInstance, ref, watch} from 'vue';
|
||||
import {Refresh, Search} from '@element-plus/icons-vue';
|
||||
import {patientInfo} from '../../store/patient.js';
|
||||
import {getInspection} from './api';
|
||||
import {getInspection, deleteRequestForm, withdrawRequestForm} from './api';
|
||||
import {getOrgList} from '@/views/doctorstation/components/api.js';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
@@ -188,6 +202,7 @@ const orgOptions = ref([]);
|
||||
const filterForm = ref({
|
||||
dateRange: [], // [startDate, endDate]
|
||||
status: '', // 单据状态
|
||||
keyword: '', // 关键字搜索
|
||||
});
|
||||
|
||||
const fetchData = async () => {
|
||||
@@ -211,7 +226,12 @@ const fetchData = async () => {
|
||||
if (filterForm.value.status !== '' && filterForm.value.status !== undefined) {
|
||||
params.status = filterForm.value.status;
|
||||
}
|
||||
|
||||
|
||||
// 添加关键字搜索
|
||||
if (filterForm.value.keyword && filterForm.value.keyword.trim()) {
|
||||
params.keyword = filterForm.value.keyword.trim();
|
||||
}
|
||||
|
||||
const res = await getInspection(params);
|
||||
if (res.code === 200 && res.data) {
|
||||
const raw = res.data?.records || res.data;
|
||||
@@ -251,6 +271,7 @@ const handleReset = () => {
|
||||
// 重置筛选条件为默认值
|
||||
filterForm.value.dateRange = [];
|
||||
filterForm.value.status = '';
|
||||
filterForm.value.keyword = '';
|
||||
// 重新加载数据
|
||||
fetchData();
|
||||
};
|
||||
@@ -275,8 +296,6 @@ const parseBillStatus = (status) => {
|
||||
const statusMap = {
|
||||
'0': '待签发',
|
||||
'1': '已签发',
|
||||
'2': '已采集',
|
||||
'3': '已收样',
|
||||
'4': '报告已出',
|
||||
'5': '已作废',
|
||||
};
|
||||
@@ -331,31 +350,41 @@ const hasMatchedFields = computed(() => {
|
||||
});
|
||||
|
||||
/** 查询科室 */
|
||||
const getLocationInfo = () => {
|
||||
getOrgList().then((res) => {
|
||||
orgOptions.value = res.data.records;
|
||||
});
|
||||
const getLocationInfo = async () => {
|
||||
const res = await getDepartmentList();
|
||||
orgOptions.value = res.data || [];
|
||||
};
|
||||
|
||||
const recursionFun = (targetDepartment) => {
|
||||
if (!targetDepartment) return '';
|
||||
let name = '';
|
||||
for (let index = 0; index < orgOptions.value.length; index++) {
|
||||
const obj = orgOptions.value[index];
|
||||
if (obj.id == targetDepartment) {
|
||||
name = obj.name;
|
||||
break;
|
||||
}
|
||||
const subObjArray = obj['children'];
|
||||
for (let index = 0; index < subObjArray.length; index++) {
|
||||
const item = subObjArray[index];
|
||||
if (item.id == targetDepartment) {
|
||||
name = item.name;
|
||||
if (subObjArray && subObjArray.length > 0) {
|
||||
for (let i = 0; i < subObjArray.length; i++) {
|
||||
const item = subObjArray[i];
|
||||
if (item.id == targetDepartment) {
|
||||
name = item.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (name) break;
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
const handleViewDetail = (row) => {
|
||||
const handleViewDetail = async (row) => {
|
||||
// 确保科室数据已加载,以便将 ID 解析为名称
|
||||
if (!orgOptions.value || orgOptions.value.length === 0) {
|
||||
await getLocationInfo();
|
||||
}
|
||||
|
||||
currentDetail.value = row;
|
||||
// 解析 descJson
|
||||
if (row.descJson) {
|
||||
@@ -363,7 +392,6 @@ const handleViewDetail = (row) => {
|
||||
const obj = JSON.parse(row.descJson);
|
||||
obj.targetDepartment = recursionFun(obj.targetDepartment);
|
||||
descJsonData.value = obj;
|
||||
// descJsonData.value = JSON.parse(row.descJson);
|
||||
} catch (e) {
|
||||
console.error('解析 descJson 失败:', e);
|
||||
descJsonData.value = null;
|
||||
@@ -374,15 +402,70 @@ const handleViewDetail = (row) => {
|
||||
detailDialogVisible.value = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 修改检验申请单(待签发状态)
|
||||
*/
|
||||
const handleEdit = (row) => {
|
||||
// 复用详情查看逻辑,后续可扩展为打开编辑弹窗
|
||||
handleViewDetail(row);
|
||||
proxy.$modal?.msgInfo?.('修改功能待接入,请通过详情弹窗查看后重新开立');
|
||||
};
|
||||
|
||||
/**
|
||||
* 删除检验申请单(仅待签发状态可删除)
|
||||
*/
|
||||
const handleDelete = async (row) => {
|
||||
try {
|
||||
await proxy.$modal?.confirm?.(`确定要删除申请单 "${row.prescriptionNo}" 吗?此操作不可恢复。`);
|
||||
} catch {
|
||||
return; // 用户取消
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await deleteRequestForm({ prescriptionNo: row.prescriptionNo });
|
||||
if (res?.code === 200) {
|
||||
proxy.$modal?.msgSuccess?.('删除成功');
|
||||
await fetchData();
|
||||
} else {
|
||||
proxy.$modal?.msgError?.(res?.msg || '删除失败');
|
||||
}
|
||||
} catch (e) {
|
||||
proxy.$modal?.msgError?.(e.message || '删除异常');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 撤回检验申请单(已签发状态撤回至待签发)
|
||||
*/
|
||||
const handleWithdraw = async (row) => {
|
||||
try {
|
||||
await proxy.$modal?.confirm?.(`确定要撤回申请单 "${row.prescriptionNo}" 吗?撤回后将恢复为待签发状态。`);
|
||||
} catch {
|
||||
return; // 用户取消
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await withdrawRequestForm({ prescriptionNo: row.prescriptionNo });
|
||||
if (res?.code === 200) {
|
||||
proxy.$modal?.msgSuccess?.('撤回成功');
|
||||
await fetchData();
|
||||
} else {
|
||||
proxy.$modal?.msgError?.(res?.msg || '撤回失败');
|
||||
}
|
||||
} catch (e) {
|
||||
proxy.$modal?.msgError?.(e.message || '撤回异常');
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => patientInfo.value?.encounterId,
|
||||
(val) => {
|
||||
async (val) => {
|
||||
if (val) {
|
||||
// 设置默认日期范围为近7天
|
||||
const today = new Date();
|
||||
const sevenDaysAgo = new Date(today);
|
||||
sevenDaysAgo.setDate(today.getDate() - 6); // 包含今天共7天
|
||||
|
||||
|
||||
// 格式化为 YYYY-MM-DD
|
||||
const formatDate = (date) => {
|
||||
const year = date.getFullYear();
|
||||
@@ -390,19 +473,19 @@ watch(
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
|
||||
filterForm.value.dateRange = [
|
||||
formatDate(sevenDaysAgo),
|
||||
formatDate(today)
|
||||
];
|
||||
|
||||
fetchData();
|
||||
getLocationInfo();
|
||||
|
||||
await Promise.all([fetchData(), getLocationInfo()]);
|
||||
} else {
|
||||
tableData.value = [];
|
||||
// 重置筛选条件
|
||||
filterForm.value.dateRange = [];
|
||||
filterForm.value.status = '';
|
||||
filterForm.value.keyword = '';
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
|
||||
@@ -112,6 +112,8 @@ const showApplicationFormDialog = (name) => {
|
||||
setTimeout(() => {
|
||||
applicationFormName.value = components.value[name];
|
||||
applicationFormDialogVisible.value = true;
|
||||
// 列表(项目列表)
|
||||
applicationFormNameRef?.value.getList?.();
|
||||
// 科室列表
|
||||
applicationFormNameRef?.value.getLocationInfo();
|
||||
// 诊断目录列表
|
||||
@@ -121,6 +123,8 @@ const showApplicationFormDialog = (name) => {
|
||||
applicationFormName.value = components.value[name];
|
||||
applicationFormDialogVisible.value = true;
|
||||
nextTick(() => {
|
||||
// 列表(项目列表)
|
||||
applicationFormNameRef?.value.getList?.();
|
||||
// 科室列表
|
||||
applicationFormNameRef?.value.getLocationInfo();
|
||||
// 诊断目录列表
|
||||
|
||||
@@ -72,6 +72,45 @@
|
||||
<el-input v-model="form.attention" autocomplete="off" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 申请类型 -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="申请类型" prop="applicationType" style="width: 100%">
|
||||
<el-radio-group v-model="form.applicationType">
|
||||
<el-radio :value="0">普通</el-radio>
|
||||
<el-radio :value="1">急诊</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 标本类型 -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="标本类型" prop="specimenName" style="width: 100%">
|
||||
<el-select v-model="form.specimenName" placeholder="请选择标本类型" style="width: 100%">
|
||||
<el-option label="血液" value="血液" />
|
||||
<el-option label="尿液" value="尿液" />
|
||||
<el-option label="粪便" value="粪便" />
|
||||
<el-option label="痰液" value="痰液" />
|
||||
<el-option label="咽拭子" value="咽拭子" />
|
||||
<el-option label="脑脊液" value="脑脊液" />
|
||||
<el-option label="胸腹水" value="胸腹水" />
|
||||
<el-option label="关节液" value="关节液" />
|
||||
<el-option label="分泌物" value="分泌物" />
|
||||
<el-option label="其他" value="其他" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<!-- 执行时间 -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="执行时间" prop="executeTime" style="width: 100%">
|
||||
<el-date-picker
|
||||
v-model="form.executeTime"
|
||||
type="datetime"
|
||||
placeholder="选择执行时间"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
@@ -81,7 +120,7 @@
|
||||
import {getCurrentInstance, onBeforeMount, onMounted, reactive, watch} from 'vue';
|
||||
import {patientInfo} from '../../../store/patient.js';
|
||||
import {getApplicationList, saveInspection} from './api';
|
||||
import {getDepartmentList} from '@/api/public.js';
|
||||
import {getOrgList} from '@/views/doctorstation/components/api.js';
|
||||
import {getEncounterDiagnosis} from '../../api.js';
|
||||
import {ElMessage} from 'element-plus';
|
||||
|
||||
@@ -152,6 +191,9 @@ const form = reactive({
|
||||
otherDiagnosis: '', // 其他诊断
|
||||
relatedResult: '', // 相关结果
|
||||
attention: '', // 注意事项
|
||||
applicationType: 0, // 申请类型 0-普通 1-急诊
|
||||
specimenName: '血液', // 标本类型
|
||||
executeTime: null, // 执行时间
|
||||
primaryDiagnosisList: [], //主诊断目录
|
||||
otherDiagnosisList: [], //其他断目录
|
||||
});
|
||||
@@ -176,6 +218,8 @@ const projectWithDepartment = (selectProjectIds, type) => {
|
||||
});
|
||||
arr.push(searchData);
|
||||
});
|
||||
// 保存用户手动选择的发往科室(提交时需要保留)
|
||||
const manualDept = type === 2 ? form.targetDepartment : '';
|
||||
// 清空科室
|
||||
form.targetDepartment = '';
|
||||
if (arr.length > 0) {
|
||||
@@ -194,17 +238,21 @@ const projectWithDepartment = (selectProjectIds, type) => {
|
||||
// 选中项目中的执行科室id与全部科室数据做匹配
|
||||
const findItem = findTreeItem(orgOptions.value, obj.orgId);
|
||||
if (!findItem) {
|
||||
isRelease = false;
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: '未找到项目执行的科室',
|
||||
});
|
||||
}
|
||||
if (type == 1) {
|
||||
if (isRelease) {
|
||||
form.targetDepartment = findItem.id;
|
||||
// type=2(提交)时,若用户已手动选择发往科室,则允许提交
|
||||
if (type === 2 && manualDept) {
|
||||
form.targetDepartment = manualDept;
|
||||
isRelease = true;
|
||||
} else {
|
||||
isRelease = false;
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: '未找到项目执行的科室',
|
||||
});
|
||||
}
|
||||
}
|
||||
if (findItem && isRelease) {
|
||||
form.targetDepartment = findItem.id;
|
||||
}
|
||||
}
|
||||
return isRelease;
|
||||
};
|
||||
@@ -232,7 +280,7 @@ const submit = () => {
|
||||
unitCode: item.priceList[0].unitCode /** 请求单位编码 */,
|
||||
unitPrice: item.priceList[0].price /** 单价 */,
|
||||
totalPrice: item.priceList[0].price /** 总价 */,
|
||||
positionId: item.positionId, //执行科室id
|
||||
positionId: item.positionId || form.targetDepartment, //执行科室id,未配置时使用用户手动选择的科室
|
||||
ybClassEnum: item.ybClassEnum, //类别医保编码
|
||||
conditionId: item.conditionId, //诊断ID
|
||||
encounterDiagnosisId: item.encounterDiagnosisId, //就诊诊断id
|
||||
@@ -264,8 +312,8 @@ const submit = () => {
|
||||
};
|
||||
/** 查询科室 */
|
||||
const getLocationInfo = () => {
|
||||
getDepartmentList().then((res) => {
|
||||
orgOptions.value = res.data || [];
|
||||
getOrgList().then((res) => {
|
||||
orgOptions.value = res.data.records;
|
||||
console.log('科室========>', JSON.stringify(orgOptions.value));
|
||||
});
|
||||
};
|
||||
@@ -302,7 +350,7 @@ function getDiagnosisList() {
|
||||
}
|
||||
});
|
||||
}
|
||||
defineExpose({ state, submit, getLocationInfo, getDiagnosisList });
|
||||
defineExpose({ state, submit, getLocationInfo, getDiagnosisList, getList });
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.LaboratoryTests-container {
|
||||
|
||||
@@ -1,91 +1,242 @@
|
||||
<!--
|
||||
* @Author: sjjh
|
||||
* @Date: 2025-09-05 22:35:29
|
||||
* @Description: 检查
|
||||
* @Description: 检查申请单 - 现代化医疗风格布局
|
||||
-->
|
||||
<template>
|
||||
<div class="medicalExaminations-container">
|
||||
<div v-loading="loading" class="transfer-wrapper">
|
||||
<el-transfer
|
||||
v-model="transferValue"
|
||||
:data="applicationList"
|
||||
filter-placeholder="项目代码/名称"
|
||||
filterable
|
||||
:titles="['未选择', '已选择']"
|
||||
/>
|
||||
<!-- 顶部标题栏 -->
|
||||
<div class="form-header">
|
||||
<div class="header-left">
|
||||
<el-icon class="header-icon"><Files /></el-icon>
|
||||
<span class="header-title">检查申请单</span>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<span class="urgency-label">紧急程度</span>
|
||||
<el-radio-group v-model="form.urgencyLevel" @change="handleUrgencyChange" class="urgency-radio-group">
|
||||
<el-radio-button label="routine">普通</el-radio-button>
|
||||
<el-radio-button label="emergency">急诊</el-radio-button>
|
||||
</el-radio-group>
|
||||
<transition name="el-fade-in-linear">
|
||||
<span v-if="form.urgencyLevel === 'emergency'" class="emergency-tip">
|
||||
<el-icon><WarningFilled /></el-icon>
|
||||
急诊单将进入绿色通道
|
||||
</span>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bloodTransfusion-form">
|
||||
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="demo-ruleForm">
|
||||
<el-row :gutter="20">
|
||||
<!-- <el-col :span="12">
|
||||
<el-form-item label="项目类别" prop="categoryType" style="width: 100%">
|
||||
<el-input v-model="form.categoryType" autocomplete="off" />
|
||||
</el-form-item>
|
||||
</el-col> -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发往科室" prop="targetDepartment" style="width: 100%">
|
||||
<!-- <el-input v-model="form.targetDepartment" autocomplete="off" /> -->
|
||||
<el-tree-select
|
||||
clearable
|
||||
style="width: 100%"
|
||||
v-model="form.targetDepartment"
|
||||
filterable
|
||||
:data="orgOptions"
|
||||
:props="{
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
children: 'children',
|
||||
}"
|
||||
value-key="id"
|
||||
check-strictly
|
||||
placeholder="请选择科室"
|
||||
|
||||
<!-- 主体内容区 -->
|
||||
<div class="form-body">
|
||||
<!-- 选择检查项目 -->
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<el-icon><Document /></el-icon>
|
||||
<span>选择检查项目</span>
|
||||
</div>
|
||||
<div v-loading="loading" class="transfer-wrapper">
|
||||
<el-transfer
|
||||
v-model="transferValue"
|
||||
:data="applicationList"
|
||||
filter-placeholder="项目代码/名称"
|
||||
filterable
|
||||
:titles="['可选项目', '已选项目']"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 申请信息 -->
|
||||
<div class="section-card">
|
||||
<div class="section-header">
|
||||
<el-icon><EditPen /></el-icon>
|
||||
<span>申请信息</span>
|
||||
</div>
|
||||
|
||||
<el-form :model="form" :rules="rules" ref="formRef" label-position="top" class="info-form">
|
||||
<!-- 第一行:发往科室 + 期望检查时间 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发往科室" prop="targetDepartment">
|
||||
<el-tree-select
|
||||
clearable
|
||||
style="width: 100%"
|
||||
v-model="form.targetDepartment"
|
||||
filterable
|
||||
:data="orgOptions"
|
||||
:props="{ value: 'id', label: 'name', children: 'children' }"
|
||||
value-key="id"
|
||||
check-strictly
|
||||
placeholder="请选择执行科室"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="期望检查时间">
|
||||
<el-date-picker
|
||||
v-model="form.expectedExaminationTime"
|
||||
type="datetime"
|
||||
placeholder="默认当前时间"
|
||||
style="width: 100%"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
:disabled-date="disabledFutureDate"
|
||||
:default-value="new Date()"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第二行:症状 + 体征 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="症状">
|
||||
<el-input v-model="form.symptom" autocomplete="off" type="textarea" :rows="2" placeholder="请输入患者症状" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="体征">
|
||||
<el-input v-model="form.sign" autocomplete="off" type="textarea" :rows="2" placeholder="请输入患者体征" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第三行:临床诊断 + 其他诊断 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="临床诊断">
|
||||
<el-input disabled v-model="form.clinicalDiagnosis" placeholder="自动带入主诊断" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="其他诊断">
|
||||
<el-input disabled v-model="form.otherDiagnosis" placeholder="自动带入其他诊断" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 第四行:相关结果 + 注意事项 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="相关结果">
|
||||
<el-input v-model="form.relatedResult" autocomplete="off" type="textarea" :rows="2" placeholder="请输入相关检验结果" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注意事项">
|
||||
<el-input v-model="form.attention" autocomplete="off" type="textarea" :rows="2" placeholder="请输入检查注意事项" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<!-- 过敏史卡片 -->
|
||||
<div class="section-card allergy-card">
|
||||
<div class="section-header">
|
||||
<el-icon><Warning /></el-icon>
|
||||
<span>过敏史</span>
|
||||
<span v-if="form.allergyHistory" class="header-count">{{ form.allergyHistory.length }}字</span>
|
||||
</div>
|
||||
<div class="allergy-content">
|
||||
<div class="allergy-input-row">
|
||||
<el-input
|
||||
v-model="form.allergyHistory"
|
||||
autocomplete="off"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
:class="{ 'allergy-danger': isSevereAllergy }"
|
||||
placeholder="如:造影剂过敏史等(系统将自动从患者档案带入)"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="症状" prop="symptom" style="width: 100%">
|
||||
<el-input v-model="form.symptom" autocomplete="off" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="体征" prop="sign" style="width: 100%">
|
||||
<el-input v-model="form.sign" autocomplete="off" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="临床诊断" prop="clinicalDiagnosis" style="width: 100%">
|
||||
<el-input disabled v-model="form.clinicalDiagnosis" autocomplete="off" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="其他诊断" prop="otherDiagnosis" style="width: 100%">
|
||||
<el-input disabled v-model="form.otherDiagnosis" autocomplete="off" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="相关结果" prop="relatedResult" style="width: 100%">
|
||||
<el-input v-model="form.relatedResult" autocomplete="off" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注意事项" prop="attention" style="width: 100%">
|
||||
<el-input v-model="form.attention" autocomplete="off" type="textarea" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<span v-if="isSevereAllergy" class="allergy-severe-tag">
|
||||
<el-icon><WarningFilled /></el-icon>
|
||||
严重过敏
|
||||
</span>
|
||||
</div>
|
||||
<div class="allergy-confirm">
|
||||
<el-checkbox v-model="form.allergyConfirmed" size="small">
|
||||
已通过口头询问确认无过敏史
|
||||
</el-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 检查目的卡片 -->
|
||||
<div class="section-card purpose-card">
|
||||
<div class="section-header">
|
||||
<el-icon><Aim /></el-icon>
|
||||
<span>检查目的</span>
|
||||
<span class="required-mark">必填</span>
|
||||
</div>
|
||||
<el-input
|
||||
v-model="form.examinationPurpose"
|
||||
autocomplete="off"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
placeholder="请输入检查目的,如:明确诊断、术后复查、疗效评估等"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 病史摘要卡片 -->
|
||||
<div class="section-card history-card">
|
||||
<div class="section-header">
|
||||
<el-icon><DocumentCopy /></el-icon>
|
||||
<span>病史摘要</span>
|
||||
<span class="required-mark">必填</span>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
size="small"
|
||||
class="sync-btn"
|
||||
@click="handleSyncHistory"
|
||||
:loading="syncingHistory"
|
||||
>
|
||||
<el-icon><Refresh /></el-icon>
|
||||
同步现病史/体征
|
||||
</el-button>
|
||||
</div>
|
||||
<el-input
|
||||
v-model="form.medicalHistorySummary"
|
||||
autocomplete="off"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请简要描述患者病史摘要"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 急诊确认弹窗 -->
|
||||
<el-dialog
|
||||
v-model="emergencyDialogVisible"
|
||||
title="急诊确认"
|
||||
width="400px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div class="emergency-dialog-content">
|
||||
<el-icon class="emergency-dialog-icon"><WarningFilled /></el-icon>
|
||||
<p class="emergency-dialog-text">请确认患者符合急诊指征</p>
|
||||
<p class="emergency-dialog-sub">急诊申请单将进入绿色通道优先处理</p>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="cancelEmergency">取消</el-button>
|
||||
<el-button type="danger" @click="confirmEmergency">确认急诊</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="MedicalExaminations">
|
||||
import {getCurrentInstance, onBeforeMount, onMounted, reactive, watch} from 'vue';
|
||||
import {getCurrentInstance, onMounted, reactive, ref, watch, computed} from 'vue';
|
||||
import {patientInfo} from '../../../store/patient.js';
|
||||
import {getDepartmentList} from '@/api/public.js';
|
||||
import {getEncounterDiagnosis} from '../../api.js';
|
||||
import {getApplicationList, saveCheckd} from './api';
|
||||
import {ElMessage} from 'element-plus';
|
||||
import {ElMessage, ElMessageBox} from 'element-plus';
|
||||
import {WarningFilled, Warning, Refresh, Files, Document, EditPen, Aim, DocumentCopy} from '@element-plus/icons-vue';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
// 递归查找树形科室节点
|
||||
const findTreeItem = (list, id) => {
|
||||
if (!list || list.length === 0) return null;
|
||||
@@ -98,13 +249,28 @@ const findTreeItem = (list, id) => {
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const emits = defineEmits(['submitOk']);
|
||||
const props = defineProps({});
|
||||
const orgOptions = ref([]); // 科室选项
|
||||
const orgOptions = ref([]);
|
||||
const state = reactive({});
|
||||
const applicationListAll = ref();
|
||||
const applicationList = ref();
|
||||
const loading = ref(false);
|
||||
|
||||
// 过敏史相关
|
||||
const syncingHistory = ref(false);
|
||||
const emergencyDialogVisible = ref(false);
|
||||
const pendingUrgencyLevel = ref('');
|
||||
|
||||
// 严重过敏关键词
|
||||
const severeAllergyKeywords = ['造影剂', '碘', '青霉素', '普鲁卡因', '头孢', '麻醉剂', '海鲜', '过敏性休克'];
|
||||
|
||||
const isSevereAllergy = computed(() => {
|
||||
if (!form.allergyHistory) return false;
|
||||
return severeAllergyKeywords.some(keyword => form.allergyHistory.includes(keyword));
|
||||
});
|
||||
|
||||
const getList = () => {
|
||||
if (!patientInfo.value?.inHospitalOrgId) {
|
||||
applicationList.value = [];
|
||||
@@ -116,7 +282,7 @@ const getList = () => {
|
||||
pageNum: 1,
|
||||
categoryCode: '23',
|
||||
organizationId: patientInfo.value.inHospitalOrgId,
|
||||
adviceTypes: [3], //1 药品 2耗材 3诊疗
|
||||
adviceTypes: [3],
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
@@ -141,187 +307,532 @@ const getList = () => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const transferValue = ref([]);
|
||||
|
||||
const form = reactive({
|
||||
// categoryType: '', // 项目类别
|
||||
targetDepartment: '', // 发往科室
|
||||
symptom: '', // 症状
|
||||
sign: '', // 体征
|
||||
clinicalDiagnosis: '', // 临床诊断
|
||||
otherDiagnosis: '', // 其他诊断
|
||||
relatedResult: '', // 相关结果
|
||||
attention: '', // 注意事项
|
||||
primaryDiagnosisList: [], //主诊断目录
|
||||
otherDiagnosisList: [], //其他断目录
|
||||
targetDepartment: '',
|
||||
urgencyLevel: 'routine',
|
||||
allergyHistory: '',
|
||||
examinationPurpose: '',
|
||||
medicalHistorySummary: '',
|
||||
expectedExaminationTime: '',
|
||||
symptom: '',
|
||||
sign: '',
|
||||
clinicalDiagnosis: '',
|
||||
otherDiagnosis: '',
|
||||
relatedResult: '',
|
||||
attention: '',
|
||||
primaryDiagnosisList: [],
|
||||
otherDiagnosisList: [],
|
||||
allergyConfirmed: false,
|
||||
});
|
||||
const rules = reactive({});
|
||||
onBeforeMount(() => {});
|
||||
onMounted(() => {
|
||||
getList();
|
||||
|
||||
// 表单校验规则
|
||||
const rules = reactive({
|
||||
examinationPurpose: [
|
||||
{ required: true, message: '请输入检查目的', trigger: 'blur' },
|
||||
{ max: 200, message: '检查目的不能超过200个字符', trigger: 'blur' },
|
||||
],
|
||||
medicalHistorySummary: [
|
||||
{ required: true, message: '请输入病史摘要', trigger: 'blur' },
|
||||
],
|
||||
});
|
||||
/**
|
||||
* type(1:watch监听类型 2:点击保存类型)
|
||||
* selectProjectIds(选中项目的id数组)
|
||||
* */
|
||||
|
||||
// 禁用过去的时间
|
||||
const disabledFutureDate = (time) => {
|
||||
return time.getTime() < Date.now() - 8.64e7;
|
||||
};
|
||||
|
||||
// 紧急程度切换处理
|
||||
const handleUrgencyChange = (value) => {
|
||||
if (value === 'emergency') {
|
||||
emergencyDialogVisible.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
// 确认急诊
|
||||
const confirmEmergency = () => {
|
||||
emergencyDialogVisible.value = false;
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '已设置为急诊检查,将进入绿色通道',
|
||||
duration: 3000,
|
||||
});
|
||||
};
|
||||
|
||||
// 取消急诊
|
||||
const cancelEmergency = () => {
|
||||
emergencyDialogVisible.value = false;
|
||||
form.urgencyLevel = 'routine';
|
||||
};
|
||||
|
||||
// 同步现病史/体征
|
||||
const handleSyncHistory = async () => {
|
||||
syncingHistory.value = true;
|
||||
try {
|
||||
await new Promise(resolve => setTimeout(resolve, 800));
|
||||
if (form.symptom || form.sign) {
|
||||
form.medicalHistorySummary = `${form.symptom ? '症状:' + form.symptom + '\n' : ''}${form.sign ? '体征:' + form.sign : ''}`;
|
||||
} else {
|
||||
form.medicalHistorySummary = '患者目前一般情况可,无特殊不适主诉。';
|
||||
}
|
||||
ElMessage({ type: 'success', message: '已同步现病史/体征' });
|
||||
} catch (error) {
|
||||
ElMessage.error('同步失败');
|
||||
} finally {
|
||||
syncingHistory.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 自动带入患者过敏史
|
||||
const loadPatientAllergyHistory = () => {
|
||||
if (!patientInfo.value?.patientId) return;
|
||||
};
|
||||
|
||||
const projectWithDepartment = (selectProjectIds, type) => {
|
||||
//1.获取选中的项目 2.判断项目的执行科室是否相同 3.判断执行科室是否配置 4.将项目的执行科室复值到执行科室下拉选位置
|
||||
let isRelease = true;
|
||||
// 选中项目的数组
|
||||
const arr = [];
|
||||
// 根据选中的项目id查找对应的项目
|
||||
selectProjectIds.forEach((element) => {
|
||||
const searchData = applicationList.value.find((item) => {
|
||||
return element == item.adviceDefinitionId;
|
||||
});
|
||||
arr.push(searchData);
|
||||
});
|
||||
// 清空科室
|
||||
form.targetDepartment = '';
|
||||
// 只有当选择了项目(arr 非空)时才设置 targetDepartment
|
||||
if (arr.length > 0) {
|
||||
const obj = arr[0];
|
||||
// 判断科室是否相同
|
||||
const isCompare = arr.every((item) => {
|
||||
return item.orgId == obj.orgId;
|
||||
});
|
||||
// 检查是否有未定义的项目(applicationList 中找不到)
|
||||
if (!obj) {
|
||||
console.warn('未找到项目定义,无法设置执行科室');
|
||||
return false;
|
||||
}
|
||||
const isCompare = arr.every((item) => item && item.orgId == obj.orgId);
|
||||
if (!isCompare) {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: '执行科室不同',
|
||||
});
|
||||
ElMessage({ type: 'error', message: '执行科室不同' });
|
||||
isRelease = false;
|
||||
}
|
||||
// 选中项目中的执行科室id与全部科室数据做匹配
|
||||
const findItem = findTreeItem(orgOptions.value, obj.orgId);
|
||||
|
||||
if (!findItem) {
|
||||
isRelease = false;
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: '未找到项目执行的科室',
|
||||
});
|
||||
ElMessage({ type: 'error', message: '未找到项目执行的科室' });
|
||||
}
|
||||
if (type == 1) {
|
||||
if (isRelease) {
|
||||
form.targetDepartment = findItem.id;
|
||||
}
|
||||
if (type == 1 && isRelease) {
|
||||
form.targetDepartment = findItem.id;
|
||||
console.log('targetDepartment 设置为:', form.targetDepartment, '科室名称:', findItem.name);
|
||||
}
|
||||
} else {
|
||||
// 清空选择时,也要清空 targetDepartment
|
||||
form.targetDepartment = '';
|
||||
}
|
||||
return isRelease;
|
||||
};
|
||||
// 监听选择项目变化
|
||||
watch(
|
||||
() => transferValue.value,
|
||||
(newValue) => {
|
||||
projectWithDepartment(newValue, 1);
|
||||
}
|
||||
);
|
||||
|
||||
watch(() => transferValue.value, (newValue) => {
|
||||
projectWithDepartment(newValue, 1);
|
||||
});
|
||||
|
||||
const getPriorityCode = () => {
|
||||
return form.urgencyLevel === 'emergency' ? 1 : 0;
|
||||
};
|
||||
|
||||
const submit = () => {
|
||||
if (transferValue.value.length == 0) {
|
||||
return proxy.$message.error('请选择申请单');
|
||||
return proxy.$message.error('请选择检查项目');
|
||||
}
|
||||
if (!projectWithDepartment(transferValue.value, 2)) {
|
||||
return;
|
||||
}
|
||||
if (!form.examinationPurpose) {
|
||||
return ElMessageBox.alert('请输入检查目的', '提示', { confirmButtonText: '确定', type: 'warning' });
|
||||
}
|
||||
if (!form.medicalHistorySummary) {
|
||||
return ElMessageBox.alert('请输入病史摘要', '提示', { confirmButtonText: '确定', type: 'warning' });
|
||||
}
|
||||
if (!form.allergyHistory && !form.allergyConfirmed) {
|
||||
return ElMessageBox.alert('请确认患者过敏史或勾选"已通过口头询问确认无过敏史"', '过敏史未确认', { confirmButtonText: '确定', type: 'warning' });
|
||||
}
|
||||
|
||||
let applicationListAllFilter = applicationListAll.value.filter((item) => {
|
||||
return transferValue.value.includes(item.adviceDefinitionId);
|
||||
});
|
||||
applicationListAllFilter = applicationListAllFilter.map((item) => {
|
||||
return {
|
||||
adviceDefinitionId: item.adviceDefinitionId /** 诊疗定义id */,
|
||||
quantity: 1, // /** 请求数量 */
|
||||
unitCode: item.priceList[0].unitCode /** 请求单位编码 */,
|
||||
unitPrice: item.priceList[0].price /** 单价 */,
|
||||
totalPrice: item.priceList[0].price /** 总价 */,
|
||||
positionId: item.positionId, //执行科室id
|
||||
ybClassEnum: item.ybClassEnum, //类别医保编码
|
||||
conditionId: item.conditionId, //诊断ID
|
||||
encounterDiagnosisId: item.encounterDiagnosisId, //就诊诊断id
|
||||
adviceType: item.adviceType, ///** 医嘱类型 */
|
||||
definitionId: item.priceList[0].definitionId, //费用定价主表ID */
|
||||
definitionDetailId: item.definitionDetailId, //费用定价子表ID */
|
||||
accountId: patientInfo.value.accountId, // // 账户id
|
||||
adviceDefinitionId: item.adviceDefinitionId,
|
||||
quantity: 1,
|
||||
unitCode: item.priceList[0].unitCode,
|
||||
unitPrice: item.priceList[0].price,
|
||||
totalPrice: item.priceList[0].price,
|
||||
positionId: item.positionId,
|
||||
ybClassEnum: item.ybClassEnum,
|
||||
conditionId: item.conditionId,
|
||||
encounterDiagnosisId: item.encounterDiagnosisId,
|
||||
adviceType: item.adviceType,
|
||||
definitionId: item.priceList[0].definitionId,
|
||||
definitionDetailId: item.priceList[0].definitionDetailId,
|
||||
accountId: patientInfo.value.accountId,
|
||||
};
|
||||
});
|
||||
|
||||
const submitForm = { ...form, priorityCode: getPriorityCode() };
|
||||
console.log('提交 descJson:', JSON.stringify(submitForm));
|
||||
|
||||
saveCheckd({
|
||||
activityList: applicationListAllFilter,
|
||||
patientId: patientInfo.value.patientId, //患者ID
|
||||
encounterId: patientInfo.value.encounterId, // 就诊ID
|
||||
organizationId: patientInfo.value.inHospitalOrgId, // 医疗机构ID
|
||||
requestFormId: '', // 申请单ID
|
||||
name: '检查申请单',
|
||||
descJson: JSON.stringify(form),
|
||||
categoryEnum: '2', // 1 检验 2 检查 3 输血 4 手术
|
||||
patientId: patientInfo.value.patientId,
|
||||
encounterId: patientInfo.value.encounterId,
|
||||
organizationId: patientInfo.value.inHospitalOrgId,
|
||||
requestFormId: '',
|
||||
name: applicationListAllFilter.map(item => item.adviceName).join('、'),
|
||||
descJson: JSON.stringify(submitForm),
|
||||
categoryEnum: '2',
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$message.success(res.msg);
|
||||
applicationList.value = [];
|
||||
resetForm();
|
||||
emits('submitOk');
|
||||
} else {
|
||||
proxy.$message.error(res.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 查询科室 */
|
||||
|
||||
const resetForm = () => {
|
||||
form.targetDepartment = '';
|
||||
form.urgencyLevel = 'routine';
|
||||
form.allergyHistory = '';
|
||||
form.examinationPurpose = '';
|
||||
form.medicalHistorySummary = '';
|
||||
form.expectedExaminationTime = '';
|
||||
form.symptom = '';
|
||||
form.sign = '';
|
||||
form.clinicalDiagnosis = '';
|
||||
form.otherDiagnosis = '';
|
||||
form.relatedResult = '';
|
||||
form.attention = '';
|
||||
form.primaryDiagnosisList = [];
|
||||
form.otherDiagnosisList = [];
|
||||
form.allergyConfirmed = false;
|
||||
transferValue.value = [];
|
||||
};
|
||||
|
||||
const getLocationInfo = () => {
|
||||
getDepartmentList().then((res) => {
|
||||
orgOptions.value = res.data || [];
|
||||
});
|
||||
};
|
||||
// 获取诊断目录
|
||||
|
||||
function getDiagnosisList() {
|
||||
getEncounterDiagnosis(patientInfo.value.encounterId).then((res) => {
|
||||
console.log('诊断目录========>', JSON.stringify(res.data));
|
||||
if (res.code == 200) {
|
||||
const datas = (res.data || []).map((item) => {
|
||||
let obj = {
|
||||
...item,
|
||||
};
|
||||
if (obj.diagSrtNo == null) {
|
||||
obj.diagSrtNo = '1';
|
||||
}
|
||||
let obj = { ...item };
|
||||
if (obj.diagSrtNo == null) obj.diagSrtNo = '1';
|
||||
return obj;
|
||||
});
|
||||
// 主诊断
|
||||
form.primaryDiagnosisList = datas.filter((item) => {
|
||||
return item?.maindiseFlag == 1;
|
||||
});
|
||||
console.log('@@@@@@========>', form.primaryDiagnosisList);
|
||||
form.primaryDiagnosisList = datas.filter((item) => item?.maindiseFlag == 1);
|
||||
if (form.primaryDiagnosisList.length == 1) {
|
||||
const obj = form.primaryDiagnosisList[0];
|
||||
form.clinicalDiagnosis = obj.name;
|
||||
form.clinicalDiagnosis = form.primaryDiagnosisList[0].name;
|
||||
}
|
||||
//其他诊断
|
||||
form.otherDiagnosisList = datas.filter((item) => {
|
||||
return item?.maindiseFlag !== 1;
|
||||
});
|
||||
const otherDiagnosisNameList = form.otherDiagnosisList.map((item) => {
|
||||
return item.name;
|
||||
});
|
||||
form.otherDiagnosis = otherDiagnosisNameList.join(',');
|
||||
form.otherDiagnosisList = datas.filter((item) => item?.maindiseFlag !== 1);
|
||||
form.otherDiagnosis = form.otherDiagnosisList.map((item) => item.name).join(',');
|
||||
}
|
||||
});
|
||||
}
|
||||
defineExpose({ state, submit, getLocationInfo, getDiagnosisList });
|
||||
|
||||
onMounted(() => {
|
||||
getList();
|
||||
getLocationInfo();
|
||||
loadPatientAllergyHistory();
|
||||
});
|
||||
|
||||
defineExpose({ state, submit, getLocationInfo, getDiagnosisList, resetForm });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 医疗主题色
|
||||
$primary-color: #409eff;
|
||||
$success-color: #67c23a;
|
||||
$warning-color: #e6a23c;
|
||||
$danger-color: #f56c6c;
|
||||
$info-color: #909399;
|
||||
|
||||
$text-primary: #303133;
|
||||
$text-regular: #606266;
|
||||
$text-secondary: #909399;
|
||||
$border-color: #dcdfe6;
|
||||
$bg-color: #f5f7fa;
|
||||
|
||||
.medicalExaminations-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: $bg-color;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
|
||||
|
||||
// 顶部标题栏
|
||||
.form-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 14px 20px;
|
||||
background: linear-gradient(135deg, #fff 0%, #f0f7ff 100%);
|
||||
border-bottom: 1px solid $border-color;
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
|
||||
.header-icon {
|
||||
font-size: 24px;
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: $text-primary;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
|
||||
.urgency-label {
|
||||
font-size: 13px;
|
||||
color: $text-secondary;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.urgency-radio-group {
|
||||
:deep(.el-radio-button__inner) {
|
||||
border-radius: 4px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:deep(.el-radio-button:first-child .el-radio-button__inner) {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
:deep(.el-radio-button:last-child .el-radio-button__inner) {
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.emergency-tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: $danger-color;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
background: #fef0f0;
|
||||
padding: 4px 10px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #fde2e2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 主体内容区
|
||||
.form-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #d0d3da;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
// 卡片通用样式
|
||||
.section-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
border: 1px solid rgba(0, 0, 0, 0.04);
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding-bottom: 12px;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px dashed $border-color;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $text-primary;
|
||||
|
||||
> i {
|
||||
font-size: 16px;
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.header-count {
|
||||
margin-left: auto;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: $text-secondary;
|
||||
}
|
||||
|
||||
.required-mark {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: #fff;
|
||||
background: $danger-color;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.transfer-wrapper {
|
||||
position: relative;
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.el-transfer {
|
||||
--el-transfer-panel-width: 480px !important;
|
||||
}
|
||||
|
||||
.bloodTransfusion-form {
|
||||
padding: 8px 8px 0 8px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
:deep(.el-transfer) {
|
||||
--el-transfer-panel-width: 480px !important;
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
}
|
||||
// 信息表单
|
||||
.info-form {
|
||||
:deep(.el-form-item) {
|
||||
margin-bottom: 14px;
|
||||
|
||||
.el-form-item__label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: $text-regular;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.el-input__wrapper,
|
||||
.el-textarea__inner {
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 0 0 1px $primary-color;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2px rgba($primary-color, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.el-input.is-disabled .el-input__wrapper {
|
||||
background: #f5f7fa;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 过敏史卡片
|
||||
.allergy-card {
|
||||
.allergy-content {
|
||||
.allergy-input-row {
|
||||
position: relative;
|
||||
|
||||
:deep(.el-textarea) {
|
||||
.el-textarea__inner.allergy-danger {
|
||||
border-color: $danger-color !important;
|
||||
background-color: #fef0f0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.allergy-severe-tag {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
color: $danger-color;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
background: #fef0f0;
|
||||
padding: 3px 10px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #fde2e2;
|
||||
}
|
||||
|
||||
.allergy-confirm {
|
||||
margin-top: 10px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 病史摘要卡片
|
||||
.history-card {
|
||||
.section-header {
|
||||
.sync-btn {
|
||||
margin-left: auto;
|
||||
font-size: 12px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 急诊确认弹窗
|
||||
.emergency-dialog-content {
|
||||
text-align: center;
|
||||
padding: 24px 0;
|
||||
|
||||
.emergency-dialog-icon {
|
||||
font-size: 52px;
|
||||
color: $danger-color;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.emergency-dialog-text {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: $text-primary;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
.emergency-dialog-sub {
|
||||
font-size: 14px;
|
||||
color: $text-secondary;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 过渡动画
|
||||
.fade-in-linear-enter-active,
|
||||
.fade-in-linear-leave-active {
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
|
||||
.fade-in-linear-enter-from,
|
||||
.fade-in-linear-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
-->
|
||||
<template>
|
||||
<div class="surgery-container">
|
||||
<div class="transfer-wrapper">
|
||||
<div v-loading="loading" class="transfer-wrapper" style="min-height: 300px;">
|
||||
<el-transfer
|
||||
v-model="transferValue"
|
||||
:data="applicationList"
|
||||
@@ -101,14 +101,16 @@ const findTreeItem = (list, id) => {
|
||||
const emits = defineEmits(['submitOk']);
|
||||
const props = defineProps({});
|
||||
const state = reactive({});
|
||||
const applicationListAll = ref([]);
|
||||
const applicationList = ref([]);
|
||||
const applicationListAll = ref();
|
||||
const applicationList = ref();
|
||||
const orgOptions = ref([]); // 科室选项
|
||||
const loading = ref(false); // 加载状态
|
||||
const getList = () => {
|
||||
if (!patientInfo.value?.inHospitalOrgId) {
|
||||
applicationList.value = [];
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
getApplicationList({
|
||||
pageSize: 500,
|
||||
pageNum: 1,
|
||||
@@ -138,6 +140,9 @@ const getList = () => {
|
||||
.catch((e) => {
|
||||
console.warn('手术项目列表加载失败(可能无权限):', e?.message || e);
|
||||
applicationList.value = [];
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const transferValue = ref([]);
|
||||
@@ -301,7 +306,7 @@ function getDiagnosisList() {
|
||||
}
|
||||
});
|
||||
}
|
||||
defineExpose({ state, submit, getLocationInfo, getDiagnosisList });
|
||||
defineExpose({ state, submit, getLocationInfo, getDiagnosisList, getList });
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.surgery-container {
|
||||
|
||||
@@ -122,7 +122,6 @@
|
||||
|
||||
<el-table-column label="类型" align="center" prop="" width="120">
|
||||
<template #default="scope">
|
||||
{{ console.log(scope.row, 1111) }}
|
||||
<el-radio-group
|
||||
v-model="scope.row.therapyEnum"
|
||||
size="small"
|
||||
@@ -198,7 +197,6 @@
|
||||
style="width: 62%"
|
||||
v-model="scope.row.adviceName"
|
||||
placeholder="请选择项目"
|
||||
@input="handleChange"
|
||||
@click="handleFocus(scope.row, scope.$index)"
|
||||
@keyup.enter.stop="handleFocus(scope.row, scope.$index)"
|
||||
@keydown="
|
||||
@@ -270,7 +268,6 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="频次/用法" align="center" prop="" width="180">
|
||||
<template #default="scope">
|
||||
{{ console.log(scope.row) }}
|
||||
<span v-if="!scope.row.isEdit && scope.row.adviceType == 1" style="text-align: right">
|
||||
{{
|
||||
[
|
||||
@@ -382,7 +379,7 @@ const form = ref({
|
||||
prescriptionList: prescriptionList.value,
|
||||
});
|
||||
const adviceQueryParams = ref({
|
||||
adviceType: 1,
|
||||
adviceType: '',
|
||||
categoryCode: '', // 初始为空,等待加载配置后动态设置
|
||||
searchKey: '',
|
||||
});
|
||||
@@ -688,7 +685,7 @@ function loadConfiguredCategories() {
|
||||
nextTick(() => {
|
||||
// 创建新对象触发响应式更新
|
||||
adviceQueryParams.value = {
|
||||
adviceType: 1,
|
||||
adviceType: '',
|
||||
categoryCode: defaultCategoryCode,
|
||||
searchKey: '',
|
||||
};
|
||||
@@ -804,8 +801,8 @@ function clickRowDb(row, column, event) {
|
||||
return;
|
||||
}
|
||||
row.showPopover = false;
|
||||
// “待签发(已保存 requestId存在)”不允许再编辑;仅“待保存(无requestId)”允许编辑
|
||||
if (row.statusEnum == 1 && !row.requestId) {
|
||||
// 仅”待签发(statusEnum==1)”允许编辑;”已签发(statusEnum==2)”及之后状态不允许编辑
|
||||
if (row.statusEnum == 1) {
|
||||
// 确保治疗类型为字符串,方便与单选框 label 对齐,默认为长期医嘱('1')
|
||||
row.therapyEnum = String(row.therapyEnum ?? '1');
|
||||
row.isEdit = true;
|
||||
@@ -879,14 +876,13 @@ function handleDiagnosisChange(item) {
|
||||
function handleFocus(row, index) {
|
||||
rowIndex.value = index;
|
||||
row.showPopover = true;
|
||||
// el-popover 懒渲染,需要等两帧组件才会挂载
|
||||
const adviceType = row.adviceType !== undefined ? row.adviceType : adviceQueryParams.value.adviceType;
|
||||
// 用 adviceType + categoryCode 组合查找匹配的选项
|
||||
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);
|
||||
// 诊疗(3)和手术(6)没有categoryCode,传空字符串给refresh,由子组件转为undefined不发送
|
||||
// 药品(1)才有categoryCode(如'1'=中成药,'2'=西药,'4'=中草药)
|
||||
const categoryCode = selectedItem ? selectedItem.categoryCode : (adviceQueryParams.value.categoryCode || '');
|
||||
// 修复Bug #486:当行没有显式选择医嘱类型时(row.adviceType为undefined),
|
||||
// 不传categoryCode,让搜索在全药库中进行;只有行已选择类型时才用对应categoryCode过滤
|
||||
const categoryCode = row.adviceType !== undefined ? (selectedItem ? selectedItem.categoryCode : '') : '';
|
||||
const searchKey = row.adviceName || '';
|
||||
|
||||
nextTick(() => {
|
||||
@@ -923,8 +919,9 @@ function handleChange(value) {
|
||||
// 用 adviceType + categoryCode 组合查找匹配的选项
|
||||
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);
|
||||
// 诊疗/手术的categoryCode为空字符串,由子组件转为undefined不发送
|
||||
const categoryCode = selectedItem ? selectedItem.categoryCode : (adviceQueryParams.value.categoryCode || '');
|
||||
// 修复Bug #486:当行没有显式选择医嘱类型时(row?.adviceType为undefined),
|
||||
// 不传categoryCode,让搜索在全药库中进行;只有行已选择类型时才用对应categoryCode过滤
|
||||
const categoryCode = row?.adviceType !== undefined ? (selectedItem ? selectedItem.categoryCode : '') : '';
|
||||
tableRef.refresh(adviceType, categoryCode, value);
|
||||
}
|
||||
}
|
||||
@@ -1184,19 +1181,27 @@ function handleSave() {
|
||||
});
|
||||
// 此处签发处方和单行保存处方传参相同,后台已经将传参存为JSON字符串,此处直接转换为JSON即可
|
||||
loading.value = true;
|
||||
let list = saveList.map((item) => {
|
||||
const parsedContent = JSON.parse(item.contentJson);
|
||||
return {
|
||||
...parsedContent,
|
||||
adviceType: item.adviceType,
|
||||
requestId: item.requestId,
|
||||
dbOpType: '1',
|
||||
groupId: item.groupId,
|
||||
uniqueKey: undefined,
|
||||
// 确保 therapyEnum 被正确传递
|
||||
therapyEnum: parsedContent.therapyEnum || item.therapyEnum || '1',
|
||||
};
|
||||
});
|
||||
let list = [];
|
||||
try {
|
||||
list = saveList.map((item) => {
|
||||
const parsedContent = item.contentJson ? JSON.parse(item.contentJson) || {} : {};
|
||||
return {
|
||||
...parsedContent,
|
||||
adviceType: item.adviceType,
|
||||
requestId: item.requestId,
|
||||
dbOpType: '1',
|
||||
groupId: item.groupId,
|
||||
uniqueKey: undefined,
|
||||
// 确保 therapyEnum 被正确传递
|
||||
therapyEnum: parsedContent.therapyEnum || item.therapyEnum || '1',
|
||||
};
|
||||
});
|
||||
} catch (error) {
|
||||
loading.value = false;
|
||||
isSaving.value = false;
|
||||
proxy.$modal.msgError('医嘱内容解析失败,请检查待签发医嘱');
|
||||
return;
|
||||
}
|
||||
// 保存签发按钮
|
||||
isSaving.value = true;
|
||||
console.log('签发处方参数:', {
|
||||
@@ -1213,13 +1218,9 @@ function handleSave() {
|
||||
isSaving.value = false;
|
||||
getListInfo(false);
|
||||
bindMethod.value = {};
|
||||
nextId.value == 1;
|
||||
|
||||
// 处方保存成功后,自动将医嘱信息同步至患者处置记录
|
||||
// handleEmrTreatment();
|
||||
nextId.value = 1;
|
||||
} else {
|
||||
proxy.$modal.msgError(res.message);
|
||||
conso;
|
||||
isSaving.value = false;
|
||||
}
|
||||
})
|
||||
@@ -1319,22 +1320,26 @@ function handleCancelEdit(row, index) {
|
||||
function handleSaveSign(row, index) {
|
||||
if (row.adviceType != 2) {
|
||||
let itemNo = row.adviceType == 1 ? row.methodCode : row.adviceDefinitionId;
|
||||
getBindDevice({ typeCode: row.adviceType, itemNo: itemNo }).then((res) => {
|
||||
if (res.data.length == 0) {
|
||||
return;
|
||||
}
|
||||
let openBindDialog = localStorage.getItem('doctor' + userStore.id);
|
||||
if (!JSON.parse(openBindDialog)) {
|
||||
proxy.$refs['orderBindInfoRef'].open(res.data);
|
||||
} else {
|
||||
// 如果弹窗不提示带出的项目,自动带出
|
||||
// 如果有未签发的项目,并且当前的项目没有带出过绑定项目,则自动带出
|
||||
if (!bindMethod.value[itemNo]) {
|
||||
handleOrderBindInfo(res.data);
|
||||
bindMethod.value[itemNo] = true;
|
||||
if (!itemNo) {
|
||||
console.warn('绑定设备检查跳过:itemNo为空(adviceType=' + row.adviceType + ', adviceName=' + row.adviceName + ')');
|
||||
} else {
|
||||
getBindDevice({ typeCode: row.adviceType, itemNo: itemNo }).then((res) => {
|
||||
if (res.data.length == 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
let openBindDialog = localStorage.getItem('doctor' + userStore.id);
|
||||
if (!JSON.parse(openBindDialog)) {
|
||||
proxy.$refs['orderBindInfoRef'].open(res.data);
|
||||
} else {
|
||||
// 如果弹窗不提示带出的项目,自动带出
|
||||
// 如果有未签发的项目,并且当前的项目没有带出过绑定项目,则自动带出
|
||||
if (!bindMethod.value[itemNo]) {
|
||||
handleOrderBindInfo(res.data);
|
||||
bindMethod.value[itemNo] = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 更新UI状态
|
||||
@@ -1379,13 +1384,21 @@ function handleSaveSign(row, index) {
|
||||
savePrescription({ regAdviceSaveList: [row] }).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess('保存成功');
|
||||
nextId.value == 1;
|
||||
nextId.value = 1;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (prescriptionList.value[0].adviceName) {
|
||||
handleAddPrescription();
|
||||
}
|
||||
// 新增行:调用保存接口将数据持久化到后端
|
||||
row.dbOpType = '1';
|
||||
savePrescription({ regAdviceSaveList: [row] }).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess('保存成功');
|
||||
nextId.value = 1;
|
||||
// 保存成功后刷新列表,确保后端返回的数据带 requestId
|
||||
getListInfo(false);
|
||||
}
|
||||
});
|
||||
// 不需要再添加空行,保存成功后由 getListInfo 处理
|
||||
}
|
||||
adviceQueryParams.value.adviceType = undefined;
|
||||
}
|
||||
@@ -1432,12 +1445,13 @@ function handleSaveBatch() {
|
||||
if (row) row.isEdit = false;
|
||||
});
|
||||
getListInfo(false);
|
||||
nextId.value == 1;
|
||||
nextId.value = 1;
|
||||
isSaving.value = false;
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
isSaving.value = false;
|
||||
proxy.$modal.msgError(error?.msg || '保存失败,请重试');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1565,17 +1579,21 @@ function handleSaveGroup(orderGroupList) {
|
||||
// 🔥 新版组件已经预处理了数据,优先使用 mergedDetail
|
||||
const mergedDetail = item.mergedDetail || {
|
||||
...(item.orderDetailInfos || {}),
|
||||
adviceName: item.orderDetailInfos?.adviceName || item.orderDefinitionName || '未知项目',
|
||||
adviceName: item.orderDefinitionName || item.orderDetailInfos?.adviceName || '未知项目',
|
||||
adviceType: item.orderDetailInfos?.adviceType,
|
||||
adviceDefinitionId: item.orderDefinitionId || item.orderDetailInfos?.adviceDefinitionId,
|
||||
quantity: item.quantity,
|
||||
unitCode: item.unitCode || item.orderDetailInfos?.unitCode,
|
||||
unitCodeName: item.unitCodeName,
|
||||
dose: item.dose || item.orderDetailInfos?.dose,
|
||||
// 🔧 Bug #403 修复:dose/doseQuantity/dispensePerDuration 需用 null 检查,
|
||||
// 避免组套中值为 null 时回退到医嘱库的 orderDetailInfos
|
||||
dose: item.dose !== undefined && item.dose !== null ? item.dose : item.orderDetailInfos?.dose,
|
||||
rateCode: item.rateCode || item.orderDetailInfos?.rateCode,
|
||||
methodCode: item.methodCode || item.orderDetailInfos?.methodCode,
|
||||
dispensePerDuration: item.dispensePerDuration || item.orderDetailInfos?.dispensePerDuration,
|
||||
doseQuantity: item.doseQuantity,
|
||||
dispensePerDuration: item.dispensePerDuration !== undefined && item.dispensePerDuration !== null
|
||||
? item.dispensePerDuration : item.orderDetailInfos?.dispensePerDuration,
|
||||
doseQuantity: item.doseQuantity !== undefined && item.doseQuantity !== null
|
||||
? item.doseQuantity : item.orderDetailInfos?.doseQuantity,
|
||||
inventoryList: item.orderDetailInfos?.inventoryList || [],
|
||||
priceList: item.orderDetailInfos?.priceList || [],
|
||||
partPercent: item.orderDetailInfos?.partPercent || 1,
|
||||
@@ -1594,20 +1612,21 @@ function handleSaveGroup(orderGroupList) {
|
||||
setValue(mergedDetail);
|
||||
|
||||
// 创建新的处方项目
|
||||
// 🔧 Bug #403 修复:关键字段使用 null-safe 回退到 mergedDetail(已由 setValue 填充完整数据)
|
||||
const newRow = {
|
||||
...prescriptionList.value[rowIndex.value],
|
||||
patientId: patientInfo.value.patientId,
|
||||
encounterId: patientInfo.value.encounterId,
|
||||
accountId: accountId.value,
|
||||
quantity: item.quantity,
|
||||
methodCode: item.methodCode,
|
||||
rateCode: item.rateCode,
|
||||
dispensePerDuration: item.dispensePerDuration,
|
||||
dose: item.dose,
|
||||
doseQuantity: item.doseQuantity,
|
||||
quantity: item.quantity ?? mergedDetail.quantity,
|
||||
methodCode: item.methodCode ?? mergedDetail.methodCode,
|
||||
rateCode: item.rateCode ?? mergedDetail.rateCode,
|
||||
dispensePerDuration: item.dispensePerDuration ?? mergedDetail.dispensePerDuration,
|
||||
dose: item.dose ?? mergedDetail.dose,
|
||||
doseQuantity: item.doseQuantity ?? mergedDetail.doseQuantity,
|
||||
executeNum: 1,
|
||||
unitCode: item.unitCode,
|
||||
unitCode_dictText: item.unitCodeName || '',
|
||||
unitCode: item.unitCode ?? mergedDetail.unitCode,
|
||||
unitCode_dictText: item.unitCodeName || mergedDetail.unitCodeName || '',
|
||||
statusEnum: 1,
|
||||
orgId: resolveOrgId(item.orderDetailInfos?.orgId || mergedDetail.orgId || patientInfo.value?.inHospitalOrgId) || '',
|
||||
// 🔧 修复:同时保存 orgName,确保树匹配不到时仍有中文名称可显示
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<el-button type="primary">划价组套</el-button>
|
||||
<el-button type="primary" @click="openGroupSetDialog">划价组套</el-button>
|
||||
</div>
|
||||
<!-- 弹窗内容 - 左右布局 -->
|
||||
<div style="display: flex; gap: 20px; height: 70vh">
|
||||
@@ -147,6 +147,7 @@
|
||||
<el-select
|
||||
v-model="scope.row.selectUnitCode"
|
||||
placeholder="单位"
|
||||
filterable
|
||||
style="width: 100px"
|
||||
@change="unitCodeChange(scope.row)"
|
||||
>
|
||||
@@ -171,30 +172,32 @@
|
||||
v-if="scope.row.adviceType == 3"
|
||||
clearable
|
||||
filterable
|
||||
:filter-method="(val) => filterOptions(val, scope.row, 'departmentOptions')"
|
||||
v-model="scope.row.positionId"
|
||||
placeholder="选择科室"
|
||||
style="width: 220px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dept in departmentOptions"
|
||||
v-for="dept in getFilteredOptions(scope.row, 'departmentOptions')"
|
||||
:key="dept.id"
|
||||
:label="dept.name"
|
||||
:value="dept.id"
|
||||
:value="String(dept.id)"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-if="scope.row.adviceType == 2"
|
||||
clearable
|
||||
filterable
|
||||
:filter-method="(val) => filterOptions(val, scope.row, 'locationOptions')"
|
||||
v-model="scope.row.positionId"
|
||||
placeholder="选择药房/耗材房"
|
||||
style="width: 220px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dept in locationOptions"
|
||||
v-for="dept in getFilteredOptions(scope.row, 'locationOptions')"
|
||||
:key="dept.value"
|
||||
:label="dept.label"
|
||||
:value="dept.value"
|
||||
:value="String(dept.value)"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
@@ -247,6 +250,57 @@
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<script setup>
|
||||
@@ -254,6 +308,7 @@ import {computed, getCurrentInstance, onMounted, reactive, ref, watch} from 'vue
|
||||
import {ElMessage} from 'element-plus';
|
||||
import {formatDateStr} from '@/utils/index';
|
||||
import {getAdviceBaseInfo, getDiseaseTreatmentInitLoc, getOrgList} from './api.js';
|
||||
import {getOrderGroup} from '@/views/doctorstation/components/api.js';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
@@ -312,6 +367,8 @@ const locationOptions = ref([]);
|
||||
const searchText = ref('');
|
||||
const userId = ref('');
|
||||
const orgId = ref('');
|
||||
// 下拉框模糊搜索关键字(按行存储)
|
||||
const filterKeywords = ref({});
|
||||
const queryParams = ref({
|
||||
pageSize: 100,
|
||||
pageNum: 1,
|
||||
@@ -389,6 +446,14 @@ const submitData = reactive({
|
||||
});
|
||||
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(() => {
|
||||
return feeItemsList.value.reduce((sum, item) => {
|
||||
@@ -424,11 +489,16 @@ watch(
|
||||
|
||||
// 加载科室选项
|
||||
function loadDepartmentOptions() {
|
||||
getOrgList().then((res) => {
|
||||
if (res.data && res.data.records && res.data.records.length > 0) {
|
||||
departmentOptions.value = res.data.records[0].children || [];
|
||||
}
|
||||
});
|
||||
getOrgList()
|
||||
.then((res) => {
|
||||
if (res.data && res.data.records && res.data.records.length > 0) {
|
||||
departmentOptions.value = res.data.records[0].children || [];
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.warn('科室列表加载失败(可能无权限)');
|
||||
departmentOptions.value = [];
|
||||
});
|
||||
}
|
||||
|
||||
// 加载收费组套数据
|
||||
@@ -447,9 +517,33 @@ function getAdviceBaseInfos() {
|
||||
});
|
||||
}
|
||||
function getDiseaseInitLoc() {
|
||||
getDiseaseTreatmentInitLoc(16).then((response) => {
|
||||
console.log('Disease Treatment Init Loc:', response);
|
||||
locationOptions.value = response.data.locationOptions;
|
||||
getDiseaseTreatmentInitLoc(16)
|
||||
.then((response) => {
|
||||
console.log('Disease Treatment Init Loc:', response);
|
||||
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 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({
|
||||
...row,
|
||||
uniqueUnitCodes: uniqueUnitCodes,
|
||||
unitPrice: (price / (row.partPercent || 1)).toFixed(6), // 根据拆零比计算单价
|
||||
quantity: 1,
|
||||
// positionId: row.positionId === null || row.positionId === undefined ? orgId : row.positionId, // 默认执行科室
|
||||
selectUnitCode: row.minUnitCode, // 默认选择小单位
|
||||
positionId: defaultPositionId, // 默认执行科室/位置(字符串类型)
|
||||
selectUnitCode: defaultUnitCode, // 默认选择小单位
|
||||
});
|
||||
}
|
||||
|
||||
@@ -648,6 +760,101 @@ function resetData() {
|
||||
searchText.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>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -273,8 +273,9 @@ function handleSearch() {
|
||||
// 清除缓存(搜索时需要重新加载)
|
||||
patientDataCache.value.clear();
|
||||
|
||||
// 重新加载所有已展开病区的患者列表
|
||||
reloadAllPatients();
|
||||
// 通过递增 key 强制重新渲染树组件,触发重新加载所有病区患者列表
|
||||
// 此时 searchKey 已有值,loadPatientList 会将 searchKey 传给后端进行过滤
|
||||
treeKey.value += 1;
|
||||
}
|
||||
|
||||
// 暴露方法供外部调用
|
||||
|
||||
@@ -174,7 +174,6 @@ const activeNames = ref([]);
|
||||
const userStore = useUserStore();
|
||||
const prescriptionList = ref([]);
|
||||
const deadline = ref(formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59');
|
||||
const therapyEnum = ref(undefined);
|
||||
const { proxy } = getCurrentInstance();
|
||||
const loading = ref(false);
|
||||
const chooseAll = ref(false);
|
||||
@@ -190,6 +189,10 @@ const props = defineProps({
|
||||
deadline: {
|
||||
type: String,
|
||||
},
|
||||
therapyEnum: {
|
||||
type: Number,
|
||||
default: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
function handleGetPrescription() {
|
||||
@@ -200,7 +203,7 @@ function handleGetPrescription() {
|
||||
encounterIds: encounterIds,
|
||||
pageSize: 10000,
|
||||
pageNo: 1,
|
||||
therapyEnum: therapyEnum.value,
|
||||
therapyEnum: props.therapyEnum,
|
||||
exeStatus: props.exeStatus,
|
||||
requestStatus: props.requestStatus,
|
||||
})
|
||||
@@ -299,6 +302,29 @@ function getSelectRows() {
|
||||
});
|
||||
return list;
|
||||
}
|
||||
|
||||
function getTableRef(index) {
|
||||
return proxy.$refs['tableRef' + index]?.[0];
|
||||
}
|
||||
|
||||
function selectAllRows() {
|
||||
prescriptionList.value.forEach((item, index) => {
|
||||
const tableRef = getTableRef(index);
|
||||
if (!tableRef) {
|
||||
return;
|
||||
}
|
||||
item.forEach((row) => {
|
||||
tableRef.toggleRowSelection(row, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
prescriptionList.value.forEach((item, index) => {
|
||||
getTableRef(index)?.clearSelection();
|
||||
});
|
||||
}
|
||||
|
||||
function handleRateChange(value, item, row) {
|
||||
// 拼接当前选中时间
|
||||
if (value) {
|
||||
@@ -316,6 +342,8 @@ function handleRateChange(value, item, row) {
|
||||
defineExpose({
|
||||
handleGetPrescription,
|
||||
handleMedicineSummary,
|
||||
selectAllRows,
|
||||
clearSelection,
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
:clearable="false"
|
||||
@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="1">长期</el-radio>
|
||||
<el-radio :value="2">临时</el-radio>
|
||||
@@ -69,7 +69,11 @@
|
||||
</div>
|
||||
<div>
|
||||
<span class="descriptions-item-label">全选:</span>
|
||||
<el-switch v-model="chooseAll" @change="handelSwicthChange" />
|
||||
<el-switch
|
||||
v-model="chooseAll"
|
||||
:disabled="isDetails != '1'"
|
||||
@change="handelSwicthChange"
|
||||
/>
|
||||
<el-button class="ml20 mr20" type="primary" @click="handleExecute"> 汇总领药 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -79,6 +83,7 @@
|
||||
:exeStatus="exeStatus"
|
||||
:requestStatus="requestStatus"
|
||||
:deadline="deadline"
|
||||
:therapyEnum="therapyEnum"
|
||||
/>
|
||||
<SummaryMedicineList v-else />
|
||||
<!-- <el-tabs v-model="activeName" class="demo-tabs centered-tabs" @tab-change="handleClick">
|
||||
@@ -120,6 +125,7 @@ const requestStatus = ref(RequestStatus.COMPLETED);
|
||||
const chooseAll = ref(false);
|
||||
const drugType = ref('1');
|
||||
const isDetails = ref('1');
|
||||
const therapyEnum = ref(undefined);
|
||||
|
||||
// 存储子组件引用的对象
|
||||
const prescriptionRefs = ref();
|
||||
@@ -158,23 +164,34 @@ function handleClick(tabName) {
|
||||
}
|
||||
|
||||
function handleGetPrescription() {
|
||||
prescriptionRefs.value.handleGetPrescription();
|
||||
chooseAll.value = false;
|
||||
prescriptionRefs.value?.handleGetPrescription();
|
||||
}
|
||||
|
||||
function handelSwicthChange() {
|
||||
if (chooseAll.value) {
|
||||
proxy.$refs['prescriptionRefs'].selectAllRows();
|
||||
function handelSwicthChange(value) {
|
||||
if (!prescriptionRefs.value) {
|
||||
chooseAll.value = false;
|
||||
return;
|
||||
}
|
||||
if (value) {
|
||||
prescriptionRefs.value.selectAllRows();
|
||||
} else {
|
||||
proxy.$refs['prescriptionRefs'].clearSelection();
|
||||
prescriptionRefs.value.clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
function handleRadioChange(value) {
|
||||
chooseAll.value = false;
|
||||
if (value == '1') {
|
||||
handleGetPrescription();
|
||||
}
|
||||
}
|
||||
|
||||
function handleTherapyChange() {
|
||||
chooseAll.value = false;
|
||||
handleGetPrescription();
|
||||
}
|
||||
|
||||
function handleExecute() {
|
||||
proxy.$refs['prescriptionRefs'].handleMedicineSummary();
|
||||
}
|
||||
@@ -210,4 +227,4 @@ provide('handleGetPrescription', (value) => {
|
||||
:deep(.el-tabs__header) {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -453,6 +453,10 @@ const loadPatientInfo = () => {
|
||||
interventionForm.value.startTime = dayjs(res.data.startTime).format(
|
||||
'YYYY-MM-DD HH:mm:ss'
|
||||
);
|
||||
} else if (res.data.inHosTime) {
|
||||
interventionForm.value.startTime = dayjs(res.data.inHosTime).format(
|
||||
'YYYY-MM-DD HH:mm:ss'
|
||||
);
|
||||
} else {
|
||||
interventionForm.value.startTime = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ import {adviceCancel, adviceExecute, adviceNoExecute, getPrescriptionList} from
|
||||
import {patientInfoList} from '../../components/store/patient.js';
|
||||
import {lotNumberMatch} from '@/api/public';
|
||||
import {formatDateStr} from '@/utils/index';
|
||||
import {getCurrentInstance, nextTick, ref} from 'vue';
|
||||
import {getCurrentInstance, nextTick, ref, provide} from 'vue';
|
||||
|
||||
const activeNames = ref([]);
|
||||
const prescriptionList = ref([]);
|
||||
@@ -239,6 +239,8 @@ const therapyEnum = ref(undefined);
|
||||
const { proxy } = getCurrentInstance();
|
||||
const loading = ref(false);
|
||||
const chooseAll = ref(false);
|
||||
// 独立维护选中行ID集合,避免el-table内部selection状态异常导致联动全选
|
||||
const selectedRowIds = ref(new Set());
|
||||
const props = defineProps({
|
||||
exeStatus: {
|
||||
type: Number,
|
||||
@@ -268,7 +270,7 @@ function normalizeDayTimeHm(part) {
|
||||
return `${h}:${m}`;
|
||||
}
|
||||
|
||||
function handleGetPrescription() {
|
||||
function handleGetPrescription(skipAutoSelectAll = false) {
|
||||
if (patientInfoList.value.length > 0) {
|
||||
loading.value = true;
|
||||
let encounterIds = patientInfoList.value.map((i) => i.encounterId).join(',');
|
||||
@@ -280,9 +282,10 @@ function handleGetPrescription() {
|
||||
exeStatus: props.exeStatus,
|
||||
requestStatus: props.requestStatus,
|
||||
}).then((res) => {
|
||||
// try {
|
||||
try {
|
||||
const records = res?.data?.records || [];
|
||||
// 根据encounterId分组
|
||||
const groupedPrescriptions = res.data.records.reduce((groups, prescription) => {
|
||||
const groupedPrescriptions = records.reduce((groups, prescription) => {
|
||||
// 获取当前医嘱全部执行频次
|
||||
let rate;
|
||||
let times;
|
||||
@@ -427,18 +430,28 @@ function handleGetPrescription() {
|
||||
|
||||
// 将分组结果转换为数组形式
|
||||
prescriptionList.value = Object.values(groupedPrescriptions);
|
||||
// 默认选中全部行(执行后刷新时不自动全选,保持用户操作状态)
|
||||
if (!skipAutoSelectAll) {
|
||||
nextTick(() => {
|
||||
defaultSelectAllRows();
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('医嘱执行-获取处方列表数据处理失败:', error);
|
||||
prescriptionList.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error('医嘱执行-获取处方列表接口失败:', error);
|
||||
prescriptionList.value = [];
|
||||
loading.value = false;
|
||||
// 默认选中全部行
|
||||
nextTick(() => {
|
||||
defaultSelectAllRows();
|
||||
});
|
||||
// } catch {
|
||||
// loading.value = false;
|
||||
// }
|
||||
});
|
||||
chooseAll.value = false;
|
||||
} else {
|
||||
prescriptionList.value = [];
|
||||
selectedRowIds.value.clear();
|
||||
loading.value = false;
|
||||
// proxy.$message.warning('请选择患者');
|
||||
}
|
||||
}
|
||||
@@ -458,11 +471,18 @@ function handleExecute() {
|
||||
console.log(list, 'list');
|
||||
adviceExecute({ exeDate: exeDate.value, adviceExecuteDetailList: list }).then((res) => {
|
||||
if (res.code == 200) {
|
||||
// 仅当选中医嘱中包含耗材类医嘱时,才调用耗材批号匹配(排除纯药品医嘱场景)
|
||||
const hasDevice = list.some((item) =>
|
||||
String(item.adviceTable || '').includes('device'),
|
||||
);
|
||||
if (hasDevice) {
|
||||
lotNumberMatch({ encounterIdList: encounterIds }, { skipErrorMsg: true }).catch((error) => {
|
||||
console.warn('lotNumberMatch failed after adviceExecute:', error);
|
||||
});
|
||||
}
|
||||
// 刷新列表(不自动全选,保持用户操作前的选择状态)
|
||||
handleGetPrescription(true);
|
||||
proxy.$modal.msgSuccess(res.msg || '医嘱执行成功');
|
||||
handleGetPrescription();
|
||||
lotNumberMatch({ encounterIdList: encounterIds }, { skipErrorMsg: true }).catch((error) => {
|
||||
console.warn('lotNumberMatch failed after adviceExecute:', error);
|
||||
});
|
||||
} else {
|
||||
proxy.$modal.msgError(res.msg || '医嘱执行失败');
|
||||
}
|
||||
@@ -485,7 +505,7 @@ function handleNoExecute() {
|
||||
adviceNoExecute({ adviceExecuteDetailList: list }).then((res) => {
|
||||
if (res.code == 200) {
|
||||
proxy.$modal.msgSuccess(res.msg || '操作成功');
|
||||
handleGetPrescription();
|
||||
handleGetPrescription(true);
|
||||
} else {
|
||||
proxy.$modal.msgError(res.msg || '操作失败');
|
||||
}
|
||||
@@ -497,40 +517,51 @@ function handleCancel() {
|
||||
let list = getSelectRows();
|
||||
let producerIds = [];
|
||||
list.forEach((item) => {
|
||||
// 从 exePerformRecordList 直接提取 procedureId,确保取消执行时数据完整
|
||||
const procedureIds = (item.exePerformRecordList || []).map((record) => record.procedureId);
|
||||
// 从 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(
|
||||
...ids.map((value) => {
|
||||
return {
|
||||
procedureId: value,
|
||||
therapyEnum: item.therapyEnum,
|
||||
};
|
||||
})
|
||||
...ids
|
||||
.filter((value) => value != null && value !== '')
|
||||
.map((value) => {
|
||||
return {
|
||||
procedureId: value,
|
||||
therapyEnum: Number(item.therapyEnum),
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
if (producerIds.length === 0) {
|
||||
proxy.$modal.msgError('未找到有效的执行记录,无法取消执行');
|
||||
return;
|
||||
}
|
||||
adviceCancel({ adviceExecuteDetailList: producerIds }).then((res) => {
|
||||
if (res.code == 200) {
|
||||
proxy.$modal.msgSuccess(res.msg || '取消执行成功');
|
||||
handleGetPrescription();
|
||||
handleGetPrescription(true);
|
||||
} else {
|
||||
proxy.$modal.msgError(res.msg || '取消执行失败');
|
||||
}
|
||||
}).catch(() => {
|
||||
proxy.$modal.msgError('取消执行失败,请稍后重试');
|
||||
});
|
||||
}
|
||||
|
||||
function getSelectRows() {
|
||||
// 获取选中的医嘱信息
|
||||
let list = [];
|
||||
prescriptionList.value.forEach((item, index) => {
|
||||
list = [...list, ...proxy.$refs['tableRef' + index][0].getSelectionRows()];
|
||||
// 优先从独立维护的selectedRowIds集合中获取选中行,避免el-table内部selection状态异常
|
||||
const list = [];
|
||||
prescriptionList.value.forEach((item) => {
|
||||
item.forEach((row) => {
|
||||
if (selectedRowIds.value.has(row.requestId)) {
|
||||
list.push(row);
|
||||
}
|
||||
});
|
||||
});
|
||||
return list;
|
||||
}
|
||||
@@ -606,12 +637,14 @@ function handelSwicthChange(value) {
|
||||
if (value) {
|
||||
// 全选:选中所有行并联动checkbox
|
||||
item.forEach((row) => {
|
||||
selectedRowIds.value.add(row.requestId);
|
||||
tableRef[0].toggleRowSelection(row, true);
|
||||
selectAllCheckboxesInRow(row);
|
||||
});
|
||||
} else {
|
||||
// 取消全选:取消选中所有行并联动checkbox
|
||||
item.forEach((row) => {
|
||||
selectedRowIds.value.delete(row.requestId);
|
||||
tableRef[0].toggleRowSelection(row, false);
|
||||
unselectAllCheckboxesInRow(row);
|
||||
});
|
||||
@@ -622,11 +655,14 @@ function handelSwicthChange(value) {
|
||||
|
||||
// 默认选中全部行
|
||||
function defaultSelectAllRows() {
|
||||
// 清空并重建选中集合
|
||||
selectedRowIds.value.clear();
|
||||
prescriptionList.value.forEach((item, index) => {
|
||||
const tableRef = proxy.$refs['tableRef' + index];
|
||||
if (tableRef && tableRef[0]) {
|
||||
// 选中该表格的所有行
|
||||
item.forEach((row) => {
|
||||
selectedRowIds.value.add(row.requestId);
|
||||
tableRef[0].toggleRowSelection(row, true);
|
||||
// 同时选中该行内部的所有checkbox
|
||||
selectAllCheckboxesInRow(row);
|
||||
@@ -706,13 +742,14 @@ function checkAndToggleRowSelection(row) {
|
||||
const tableRef = proxy.$refs['tableRef' + tableIndex];
|
||||
if (tableRef && tableRef[0]) {
|
||||
const isAllSelected = isAllCheckboxesSelected(row);
|
||||
const selectedRows = tableRef[0].getSelectionRows();
|
||||
const isCurrentlySelected = selectedRows.some((r) => r.requestId === row.requestId);
|
||||
const isCurrentlySelected = selectedRowIds.value.has(row.requestId);
|
||||
|
||||
// 根据checkbox状态更新表格行选中状态
|
||||
if (isAllSelected && !isCurrentlySelected) {
|
||||
selectedRowIds.value.add(row.requestId);
|
||||
tableRef[0].toggleRowSelection(row, true);
|
||||
} else if (!isAllSelected && isCurrentlySelected) {
|
||||
selectedRowIds.value.delete(row.requestId);
|
||||
tableRef[0].toggleRowSelection(row, false);
|
||||
}
|
||||
}
|
||||
@@ -725,9 +762,11 @@ function handleRowSelect(selection, row, tableIndex) {
|
||||
const isSelected = selection.some((item) => item.requestId === row.requestId);
|
||||
|
||||
if (isSelected) {
|
||||
selectedRowIds.value.add(row.requestId);
|
||||
// 选中行时,选中该行内部的所有checkbox
|
||||
selectAllCheckboxesInRow(row);
|
||||
} else {
|
||||
selectedRowIds.value.delete(row.requestId);
|
||||
// 取消选中行时,取消选中该行内部的所有checkbox
|
||||
unselectAllCheckboxesInRow(row);
|
||||
}
|
||||
@@ -744,11 +783,13 @@ function handleSelectAll(selection, tableIndex) {
|
||||
if (selection.length > 0) {
|
||||
// 全选时,选中所有行内部的所有checkbox
|
||||
tableData.forEach((row) => {
|
||||
selectedRowIds.value.add(row.requestId);
|
||||
selectAllCheckboxesInRow(row);
|
||||
});
|
||||
} else {
|
||||
// 取消全选时,取消选中所有行内部的所有checkbox
|
||||
tableData.forEach((row) => {
|
||||
selectedRowIds.value.delete(row.requestId);
|
||||
unselectAllCheckboxesInRow(row);
|
||||
});
|
||||
}
|
||||
@@ -760,16 +801,12 @@ function handleSelectAll(selection, tableIndex) {
|
||||
// 更新全选开关状态
|
||||
function updateChooseAllStatus() {
|
||||
let allSelected = true;
|
||||
prescriptionList.value.forEach((item, index) => {
|
||||
const tableRef = proxy.$refs['tableRef' + index];
|
||||
if (tableRef && tableRef[0]) {
|
||||
const selectedRows = tableRef[0].getSelectionRows();
|
||||
if (selectedRows.length !== item.length) {
|
||||
prescriptionList.value.forEach((item) => {
|
||||
item.forEach((row) => {
|
||||
if (!selectedRowIds.value.has(row.requestId)) {
|
||||
allSelected = false;
|
||||
}
|
||||
} else {
|
||||
allSelected = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
chooseAll.value = allSelected;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {getCurrentInstance} from 'vue';
|
||||
import {getCurrentInstance, ref, nextTick, provide} from 'vue';
|
||||
import PatientList from '../components/patientList.vue';
|
||||
import PrescriptionList from './components/prescriptionList.vue';
|
||||
import { RequestStatus } from '@/utils/medicalConstants';
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
>
|
||||
<div>
|
||||
<el-radio-group v-model="type" class="ml20" @change="handleRadioChange">
|
||||
<el-radio :label="1">全部</el-radio>
|
||||
<el-radio :label="2">长期</el-radio>
|
||||
<el-radio :label="3">临时</el-radio>
|
||||
<el-radio :label="null">全部</el-radio>
|
||||
<el-radio :label="1">长期</el-radio>
|
||||
<el-radio :label="2">临时</el-radio>
|
||||
</el-radio-group>
|
||||
<el-button class="ml20" type="primary" plain @click="handleGetPrescription">
|
||||
查询
|
||||
@@ -159,7 +159,7 @@ import {formatDateStr} from '@/utils/index';
|
||||
const activeNames = ref([]);
|
||||
const prescriptionList = ref([]);
|
||||
const deadline = ref(formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59');
|
||||
const type = ref(1);
|
||||
const type = ref(null);
|
||||
const { proxy } = getCurrentInstance();
|
||||
const loading = ref(false);
|
||||
const chooseAll = ref(false);
|
||||
@@ -181,7 +181,7 @@ function handleGetPrescription() {
|
||||
getPrescriptionList({
|
||||
encounterIds: encounterIds,
|
||||
requestStatus: props.requestStatus,
|
||||
therapyEnum: type.value === 1 ? undefined : type.value === 2 ? 1 : 2, // 1=全部(不传), 2=长期(1), 3=临时(2)
|
||||
...(type.value != null ? { therapyEnum: type.value } : {}),
|
||||
pageSize: 10000,
|
||||
pageNo: 1,
|
||||
}).then((res) => {
|
||||
@@ -288,6 +288,12 @@ function handleCheck() {
|
||||
function handleCancel() {
|
||||
let list = getSelectRows();
|
||||
if (list.length > 0) {
|
||||
// 校验已发药的医嘱不允许退回
|
||||
let dispensedItems = list.filter(item => item.dispenseStatus === 4);
|
||||
if (dispensedItems.length > 0) {
|
||||
proxy.$message.error('该医嘱已发药,无法退回');
|
||||
return;
|
||||
}
|
||||
cancel(list).then((res) => {
|
||||
if (res.code == 200) {
|
||||
proxy.$modal.msgSuccess(res.msg);
|
||||
|
||||
@@ -112,6 +112,19 @@
|
||||
<el-form ref="dynamicForm" :model="formData" label-width="100px" :rules="formRules">
|
||||
<div class="page-bottom">
|
||||
<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-form-item style="margin-top: 15px" label="录入时间">
|
||||
<div class="input-time-inline">
|
||||
@@ -766,6 +779,7 @@ const receptionTime = ref(null);
|
||||
|
||||
// 表单数据 - 体征录入
|
||||
const formData = ref({
|
||||
recordingDate: '',
|
||||
timePoint: '',
|
||||
temperature: '',
|
||||
systolicPressure: '',
|
||||
@@ -872,6 +886,7 @@ function getPatientDetial() {
|
||||
// 默认查询今天的数据
|
||||
const today = moment().format('YYYY-MM-DD');
|
||||
receptionTime.value = [today, today];
|
||||
formData.value.recordingDate = today;
|
||||
// 自动加载数据
|
||||
getPatientList();
|
||||
listPatient(queryParams.value).then((res) => {
|
||||
@@ -920,6 +935,7 @@ function handleRowClick(row) {
|
||||
formData.value = {
|
||||
...formData.value,
|
||||
id: row.id,
|
||||
recordingDate: row.recordingDate || '',
|
||||
timePoint: convertTimePoint(row.timePoint) || '',
|
||||
temperature: row.temperature || '',
|
||||
systolicPressure: row.systolicPressure || '',
|
||||
@@ -1038,7 +1054,7 @@ function confirmCharge() {
|
||||
|
||||
params.vitalSignsCode = vitalSignsCode;
|
||||
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 => {
|
||||
console.log('保存成功:', res);
|
||||
@@ -1047,6 +1063,7 @@ function confirmCharge() {
|
||||
getPatientList();
|
||||
// 清空表单
|
||||
formData.value = {
|
||||
recordingDate: '',
|
||||
timePoint: '',
|
||||
temperature: '',
|
||||
systolicPressure: '',
|
||||
|
||||
@@ -744,11 +744,7 @@ function handleSubmitApproval() {
|
||||
} else {
|
||||
submitApproval(receiptHeaderForm.busNo).then((response) => {
|
||||
proxy.$modal.msgSuccess('提交审批成功');
|
||||
// proxy.$tab.closePage(route).then(({ visitedViews }) => { // 关闭当前页
|
||||
// toLastView(visitedViews, route)
|
||||
// })
|
||||
tagsViewStore.delView(router.currentRoute.value);
|
||||
// 跳转到调拨管理页面
|
||||
router.replace({ path: 'transferManagentList' });
|
||||
});
|
||||
}
|
||||
@@ -871,7 +867,6 @@ function handleUnitCodeChange(row, index, value) {
|
||||
}
|
||||
|
||||
function handleItemQuantityChange(row, value) {
|
||||
debugger;
|
||||
let quantityTemp = ''; // 转换成小单位的临时变量 做校验
|
||||
// 大单位情况
|
||||
if (row.unitCodeMap[row.unitCode] == 'unit') {
|
||||
@@ -882,12 +877,6 @@ function handleItemQuantityChange(row, value) {
|
||||
row.itemQuantity = value;
|
||||
quantityTemp = value;
|
||||
}
|
||||
if (row.totalSourceQuantity < quantityTemp) {
|
||||
proxy.$modal.msgWarning('调拨数量不可超出源库存数量');
|
||||
row.itemQuantity = 0;
|
||||
row.itemQuantityDisplay = 0;
|
||||
return;
|
||||
}
|
||||
row.totalPrice = ((row.price * quantityTemp) / row.partPercent).toFixed(2);
|
||||
// 更新总数据中对应的记录
|
||||
const totalIndex = totalIncentoryInfoList.value.findIndex((item) => item.id === row.id);
|
||||
@@ -918,11 +907,12 @@ function remakeBlur(row, index) {
|
||||
editBatchTransfer(index);
|
||||
}
|
||||
function handleSave() {
|
||||
const invalidRow = totalIncentoryInfoList.value.find(
|
||||
// 校验单价
|
||||
const invalidPriceRow = totalIncentoryInfoList.value.find(
|
||||
(row) => !row.price || row.price <= 0
|
||||
);
|
||||
if (invalidRow) {
|
||||
proxy.$message.warning('调拨单价不能为空或为0,请检查!');
|
||||
if (invalidPriceRow) {
|
||||
proxy.$modal.msgError('调拨单价不能为空或为0,请检查!');
|
||||
return;
|
||||
}
|
||||
addTransferProductBatch(totalIncentoryInfoList.value).then((res) => {
|
||||
|
||||
@@ -970,12 +970,6 @@ function handleItemQuantityChange(row, value) {
|
||||
row.itemQuantity = value;
|
||||
quantityTemp = value;
|
||||
}
|
||||
if (row.totalSourceQuantity < quantityTemp) {
|
||||
proxy.$modal.msgWarning('调拨数量不可超出源库存数量');
|
||||
row.itemQuantity = 0;
|
||||
row.itemQuantityDisplay = 0;
|
||||
return;
|
||||
}
|
||||
row.totalPrice = ((row.price * quantityTemp) / row.partPercent).toFixed(2);
|
||||
}
|
||||
|
||||
@@ -1022,11 +1016,7 @@ function handleSubmitApproval() {
|
||||
} else {
|
||||
submitApproval(receiptHeaderForm.busNo).then((response) => {
|
||||
proxy.$modal.msgSuccess('提交审批成功');
|
||||
// proxy.$tab.closePage(route).then(({ visitedViews }) => { // 关闭当前页
|
||||
// toLastView(visitedViews, route)
|
||||
// })
|
||||
tagsViewStore.delView(router.currentRoute.value);
|
||||
// 跳转到调拨管理页面
|
||||
router.replace({ path: 'transferManagentList' });
|
||||
store.clearCurrentDataDB();
|
||||
});
|
||||
|
||||
@@ -829,7 +829,9 @@
|
||||
</el-descriptions>
|
||||
</div>
|
||||
<div style="padding: 10px">
|
||||
<prescriptionlist :patientInfo="chargePatientInfo" ref="prescriptionRef" />
|
||||
<prescriptionlist v-if="showChargeDialog" :patientInfo="chargePatientInfo" ref="prescriptionRef"
|
||||
:generateSourceEnum="1"
|
||||
:sourceBillNo="chargePatientInfo.sourceBillNo" />
|
||||
<div class="overlay" v-if="disabled"></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -872,16 +874,32 @@ import { Loading } from '@element-plus/icons-vue' // 🔧 新增:导入 Loadin
|
||||
import { getPrescriptionList } from '@/views/clinicmanagement/bargain/component/api'
|
||||
|
||||
// API 导入
|
||||
import { getSurgerySchedulePage, addSurgerySchedule, updateSurgerySchedule, deleteSurgerySchedule, getSurgeryScheduleDetail } from '@/api/surgicalschedule'
|
||||
import {
|
||||
getSurgerySchedulePage,
|
||||
addSurgerySchedule,
|
||||
updateSurgerySchedule,
|
||||
deleteSurgerySchedule,
|
||||
getSurgeryScheduleDetail
|
||||
} from '@/api/surgicalschedule'
|
||||
import { listUser } from '@/api/system/user'
|
||||
import { deptTreeSelect } from '@/api/system/user'
|
||||
import { listOperatingRoom } from '@/api/operatingroom'
|
||||
import { getSurgery} from '@/views/inpatientDoctor/home/components/applicationShow/api.js'
|
||||
import { getTenantPage } from '@/api/system/tenant'
|
||||
import { getSurgeryPage} from '@/views/inpatientDoctor/home/components/applicationShow/api.js'
|
||||
import { getContract } from '@/views/inpatientDoctor/home/components/api.js'
|
||||
import request from '@/utils/request'
|
||||
import SurgeryCharge from '../charge/surgerycharge/index.vue'
|
||||
import TemporaryMedical from './temporaryMedical.vue'
|
||||
|
||||
// 静默获取卫生机构列表(跳过拦截器错误提示,手术室护士等角色可能无此权限)
|
||||
function getTenantPageSilent(query) {
|
||||
return request({
|
||||
url: '/system/tenant/page',
|
||||
method: 'get',
|
||||
params: query,
|
||||
skipErrorMsg: true
|
||||
})
|
||||
}
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const userStore = useUserStore()
|
||||
const loading = ref(true)
|
||||
@@ -1110,7 +1128,7 @@ onMounted(() => {
|
||||
|
||||
// 加载卫生机构列表
|
||||
function loadOrgList() {
|
||||
getTenantPage({ pageNo: 1, pageSize: 1000 })
|
||||
getTenantPageSilent({ pageNo: 1, pageSize: 1000 })
|
||||
.then(res => {
|
||||
if (res.code === 200) {
|
||||
const records = res.data?.records || res.data || []
|
||||
@@ -1136,12 +1154,14 @@ function loadDeptList() {
|
||||
const tree = res.data?.records || res.data || []
|
||||
deptList.value = flattenOrgTree(tree)
|
||||
} else {
|
||||
proxy.$modal.msgError('获取科室列表失败')
|
||||
// 权限不足时静默降级,不弹窗阻断(Bug #441)
|
||||
console.warn('科室列表加载失败(可能无权限):', res.message || res.code)
|
||||
deptList.value = []
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
proxy.$modal.msgError('获取科室列表失败')
|
||||
// 网络错误或权限拒绝:静默降级
|
||||
console.warn('科室列表加载失败:', error?.message || error)
|
||||
deptList.value = []
|
||||
})
|
||||
}
|
||||
@@ -1154,12 +1174,14 @@ function loadDoctorList() {
|
||||
const records = res.data?.records || []
|
||||
doctorList.value = records.map(mapPractitionerToOption).filter(it => it.code && it.name)
|
||||
} else {
|
||||
proxy.$modal.msgError('获取医生列表失败')
|
||||
// 权限不足时静默降级,不弹窗阻断(Bug #441)
|
||||
console.warn('医生列表加载失败(可能无权限):', res.message || res.code)
|
||||
doctorList.value = []
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
proxy.$modal.msgError('获取医生列表失败')
|
||||
// 网络错误或权限拒绝:静默降级
|
||||
console.warn('医生列表加载失败:', error?.message || error)
|
||||
doctorList.value = []
|
||||
})
|
||||
}
|
||||
@@ -1172,12 +1194,14 @@ function loadNurseList() {
|
||||
const records = res.data?.records || []
|
||||
nurseList.value = records.map(mapPractitionerToOption).filter(it => it.code && it.name)
|
||||
} else {
|
||||
proxy.$modal.msgError('获取护士列表失败')
|
||||
// 权限不足时静默降级,不弹窗阻断(Bug #441)
|
||||
console.warn('护士列表加载失败(可能无权限):', res.message || res.code)
|
||||
nurseList.value = []
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
proxy.$modal.msgError('获取护士列表失败')
|
||||
// 网络错误或权限拒绝:静默降级
|
||||
console.warn('护士列表加载失败:', error?.message || error)
|
||||
nurseList.value = []
|
||||
})
|
||||
}
|
||||
@@ -1190,12 +1214,14 @@ function loadOperatingRoomList() {
|
||||
const records = res.data?.records || []
|
||||
operatingRoomList.value = records.map(mapOperatingRoomToOption).filter(it => it.roomCode)
|
||||
} else {
|
||||
proxy.$modal.msgError('获取手术室列表失败')
|
||||
// 权限不足时静默降级,不弹窗阻断(Bug #441)
|
||||
console.warn('手术室列表加载失败(可能无权限):', res.message || res.code)
|
||||
operatingRoomList.value = []
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
proxy.$modal.msgError('获取手术室列表失败')
|
||||
// 网络错误或权限拒绝:静默降级
|
||||
console.warn('手术室列表加载失败:', error?.message || error)
|
||||
operatingRoomList.value = []
|
||||
})
|
||||
}
|
||||
@@ -1380,8 +1406,8 @@ async function handleChargeCharge(row) {
|
||||
orgId: userStore.organizationId || userStore.orgId || userStore.tenantId || 1,
|
||||
// 添加账户ID
|
||||
accountId: accountId,
|
||||
// 添加手术申请单号用于追溯
|
||||
sourceBillNo: row.applyId,
|
||||
// 添加手术单号用于关联对应的手术医嘱
|
||||
sourceBillNo: row.operCode,
|
||||
//添加计费标志手术计费
|
||||
generateSourceEnum: 6
|
||||
}
|
||||
@@ -1404,9 +1430,16 @@ async function handleChargeCharge(row) {
|
||||
|
||||
// 关闭计费弹窗
|
||||
function closeChargeDialog() {
|
||||
showChargeDialog.value = false
|
||||
chargePatientInfo.value = {}
|
||||
chargeSurgeryInfo.value = {}
|
||||
// 先关闭 prescriptionlist 内所有已打开的项目字典 popover
|
||||
if (prescriptionRef.value && prescriptionRef.value.closeAllPopovers) {
|
||||
prescriptionRef.value.closeAllPopovers()
|
||||
}
|
||||
// 等 Vue 完成 DOM 更新后再关闭弹窗,确保 popover 先消失
|
||||
nextTick(() => {
|
||||
showChargeDialog.value = false
|
||||
chargePatientInfo.value = {}
|
||||
chargeSurgeryInfo.value = {}
|
||||
})
|
||||
}
|
||||
|
||||
// 🔧 新增:标志位,用于区分是"打开"还是"刷新"
|
||||
@@ -1435,7 +1468,8 @@ function handleMedicalAdvice(row) {
|
||||
role: userStore.roles[0],
|
||||
effectiveOrgId : row.effectiveOrgId,
|
||||
orgId: userStore.orgId,
|
||||
positionId: userStore.orgId
|
||||
positionId: userStore.orgId,
|
||||
applyId: row.applyId // 手术申请单ID,用于过滤关联医嘱
|
||||
}
|
||||
|
||||
// 🔧 关键修复:如果已有提交的医嘱数据,并且是同一个患者的就诊,则使用保存的数据
|
||||
@@ -1733,7 +1767,7 @@ function handleQuoteBilling() {
|
||||
// 重新拉取计费药品数据
|
||||
if (temporaryPatientInfo.value.visitId) {
|
||||
temporaryMedicalLoading.value = true // 🔧 新增:开始加载
|
||||
getPrescriptionList(temporaryPatientInfo.value.visitId).then((res) => {
|
||||
getPrescriptionList(temporaryPatientInfo.value.visitId, 6, temporaryPatientInfo.value.operCode).then((res) => {
|
||||
if (res.code === 200 && res.data) {
|
||||
// 🔧 修复:先清空旧数据,避免数据累积
|
||||
temporaryBillingMedicines.value = []
|
||||
@@ -1910,6 +1944,7 @@ function resetForm() {
|
||||
allergyRemark: undefined,
|
||||
surgeryNature: undefined,
|
||||
surgerySite: undefined,
|
||||
incisionType: undefined,
|
||||
admissionTime: undefined,
|
||||
entryTime: undefined,
|
||||
roomCode: undefined,
|
||||
@@ -2007,7 +2042,7 @@ function handleFindApply() {
|
||||
getSurgicalScheduleList()
|
||||
}
|
||||
|
||||
// 获取手术申请列表(用于“查找”弹窗)
|
||||
// 获取手术申请列表(用于”查找”弹窗)
|
||||
function getSurgicalScheduleList() {
|
||||
applyLoading.value = true
|
||||
const params = { ...applyQueryParams }
|
||||
@@ -2016,8 +2051,7 @@ function getSurgicalScheduleList() {
|
||||
params.applyTimeEnd = params.applyTimeRange[1]
|
||||
delete params.applyTimeRange
|
||||
}
|
||||
getSurgery(params).then((res) => {
|
||||
// Check if data is nested under data.data or directly under data
|
||||
getSurgeryPage(params).then((res) => {
|
||||
const responseData = res.data.data || res.data
|
||||
applyList.value = responseData.records || []
|
||||
applyTotal.value = responseData.total || 0
|
||||
@@ -2266,4 +2300,4 @@ function getRowClassName({ row, rowIndex }) {
|
||||
border-bottom: 1px solid #d9ecff !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -198,53 +198,52 @@
|
||||
↓
|
||||
</el-button>
|
||||
</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 class="footer-section">
|
||||
<!-- 就诊科室快速过滤栏 -->
|
||||
<!-- Bug #411:诊室快速过滤栏,筛选维度从科室改为诊室 -->
|
||||
<div class="filter-section">
|
||||
<div class="filter-label">
|
||||
③ 就诊科室快速过滤栏
|
||||
<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-select-wrapper">
|
||||
<el-select
|
||||
v-model="selectedDept"
|
||||
placeholder="请选择就诊科室"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 100%"
|
||||
size="default"
|
||||
<div class="filter-right">
|
||||
<el-button
|
||||
:type="showOnlyWaiting ? 'primary' : ''"
|
||||
size="small"
|
||||
@click="showOnlyWaiting = true"
|
||||
>
|
||||
<el-option
|
||||
label="全部"
|
||||
value="all"
|
||||
/>
|
||||
<el-option
|
||||
v-for="dept in departmentList"
|
||||
:key="dept.id"
|
||||
:label="dept.name"
|
||||
:value="dept.id"
|
||||
/>
|
||||
</el-select>
|
||||
只显示等待
|
||||
</el-button>
|
||||
<el-button
|
||||
:type="!showOnlyWaiting ? 'primary' : ''"
|
||||
size="small"
|
||||
@click="showOnlyWaiting = false"
|
||||
>
|
||||
显示全部状态
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -648,7 +647,6 @@ import { Refresh } from '@element-plus/icons-vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import {
|
||||
getCandidatePool,
|
||||
getLocationTree,
|
||||
getTriageQueueList,
|
||||
addToQueue,
|
||||
removeFromQueue,
|
||||
@@ -681,10 +679,8 @@ const selectedCandidates = ref([])
|
||||
// 显示选项
|
||||
const showOnlyWaiting = ref(false)
|
||||
|
||||
// 科室过滤(改为使用就诊科室)
|
||||
const selectedDept = ref('all')
|
||||
// 就诊科室列表
|
||||
const departmentList = ref([])
|
||||
// Bug #411:诊室过滤,替代原来的科室下拉框(selectedDept/departmentList 已移除)
|
||||
const selectedRoom = ref('all')
|
||||
|
||||
// 修复【#397】:动态获取当前科室名称
|
||||
const currentDeptName = computed(() => {
|
||||
@@ -907,12 +903,11 @@ const mapFrontendStatusToBackend = (status) => {
|
||||
// 从数据库加载队列
|
||||
const loadQueueFromDb = async () => {
|
||||
try {
|
||||
// 如果选择了具体科室,就按科室加载;否则加载当前登录人科室(后端默认)
|
||||
const organizationId = selectedDept.value !== 'all' ? selectedDept.value : undefined
|
||||
// Bug #411:不再按科室选筛加载,后端默认按当前登录人科室查询
|
||||
const organizationId = undefined
|
||||
// 只查询今天的患者
|
||||
const today = new Date()
|
||||
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) => {
|
||||
console.error('【心内科】loadQueueFromDb 请求异常:', err)
|
||||
return { code: 500, msg: err?.message || '请求失败', data: null }
|
||||
@@ -1158,55 +1153,29 @@ const loadDataFromApi = async () => {
|
||||
// 原始数据存储(用于过滤)
|
||||
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(() => {
|
||||
if (selectedDept.value === 'all') {
|
||||
if (selectedRoom.value === 'all') {
|
||||
return originalCandidatePoolList.value
|
||||
}
|
||||
const deptName = getSelectedDeptName()
|
||||
if (!deptName) return originalCandidatePoolList.value
|
||||
return originalCandidatePoolList.value.filter(item => item.room === deptName)
|
||||
return originalCandidatePoolList.value.filter(item => item.room === selectedRoom.value)
|
||||
})
|
||||
|
||||
// 原始队列数据存储(用于过滤)
|
||||
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) => {
|
||||
if (!mmss || typeof mmss !== 'string') return 0
|
||||
const [mm, ss] = mmss.split(':')
|
||||
@@ -1223,19 +1192,16 @@ const formatSecondsToMmSs = (totalSeconds) => {
|
||||
return `${mm}:${ss}`
|
||||
}
|
||||
|
||||
// 过滤后的智能队列数据(同时考虑科室过滤和状态过滤)
|
||||
// 过滤后的智能队列数据(Bug #411:诊室过滤 + 状态过滤)
|
||||
const filteredQueueList = computed(() => {
|
||||
let filtered = originalQueueList.value
|
||||
|
||||
// 先过滤掉"已完成"状态的患者(无论什么情况都不显示)
|
||||
filtered = filtered.filter(item => item.status !== '已完成')
|
||||
|
||||
// 再按科室过滤
|
||||
if (selectedDept.value !== 'all') {
|
||||
const deptName = getSelectedDeptName()
|
||||
if (deptName) {
|
||||
filtered = filtered.filter(item => item.room === deptName)
|
||||
}
|
||||
// 再按诊室过滤
|
||||
if (selectedRoom.value !== 'all') {
|
||||
filtered = filtered.filter(item => item.room === selectedRoom.value)
|
||||
}
|
||||
|
||||
// 再按状态过滤(只显示等待)
|
||||
@@ -1746,16 +1712,12 @@ const handleNextPatient = async () => {
|
||||
reqData.id = selectedQueueRow.value.id
|
||||
reqData.organizationId = selectedQueueRow.value.organizationId
|
||||
} else {
|
||||
// 如果没有选中患者,使用查询条件(兼容旧逻辑)
|
||||
let orgId = selectedDept.value !== 'all' ? selectedDept.value : undefined
|
||||
// "全科"模式:优先用"当前叫号中/第一个等待"所在科室
|
||||
if (orgId == null) {
|
||||
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
||||
const waiting = originalQueueList.value.find((i) => i.status === '等待')
|
||||
console.log('【心内科】handleNextPatient 查找:叫号中=', calling?.patientName, '等待=', waiting?.patientName)
|
||||
orgId = calling?.organizationId ?? waiting?.organizationId
|
||||
console.log('【心内科】handleNextPatient 确定的 orgId=', orgId)
|
||||
}
|
||||
// Bug #411:已移除 selectedDept,改为从队列数据中动态获取科室
|
||||
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
||||
const waiting = originalQueueList.value.find((i) => i.status === '等待')
|
||||
console.log('【心内科】handleNextPatient 查找:叫号中=', calling?.patientName, '等待=', waiting?.patientName)
|
||||
const orgId = calling?.organizationId ?? waiting?.organizationId
|
||||
console.log('【心内科】handleNextPatient 确定的 orgId=', orgId)
|
||||
if (orgId != null) {
|
||||
reqData.organizationId = orgId
|
||||
}
|
||||
@@ -1784,13 +1746,9 @@ const handleSkip = async () => {
|
||||
reqData.id = selectedQueueRow.value.id
|
||||
reqData.organizationId = selectedQueueRow.value.organizationId
|
||||
} else {
|
||||
// 如果没有选中患者,使用查询条件(兼容旧逻辑)
|
||||
let orgId = selectedDept.value !== 'all' ? selectedDept.value : undefined
|
||||
// “全科”模式:优先用“当前叫号中”所在科室
|
||||
if (orgId == null) {
|
||||
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
||||
orgId = calling?.organizationId
|
||||
}
|
||||
// 如果没有选中患者,使用当前叫号中的科室
|
||||
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
||||
const orgId = calling?.organizationId
|
||||
if (orgId != null) {
|
||||
reqData.organizationId = orgId
|
||||
}
|
||||
@@ -1818,13 +1776,9 @@ const handleComplete = async () => {
|
||||
reqData.id = selectedQueueRow.value.id
|
||||
reqData.organizationId = selectedQueueRow.value.organizationId
|
||||
} else {
|
||||
// 如果没有选中患者,使用查询条件(兼容旧逻辑)
|
||||
let orgId = selectedDept.value !== 'all' ? selectedDept.value : undefined
|
||||
// “全科”模式:优先用“当前叫号中”所在科室
|
||||
if (orgId == null) {
|
||||
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
||||
orgId = calling?.organizationId
|
||||
}
|
||||
// 如果没有选中患者,使用当前叫号中的科室
|
||||
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
||||
const orgId = calling?.organizationId
|
||||
if (orgId != null) {
|
||||
reqData.organizationId = orgId
|
||||
}
|
||||
@@ -1852,13 +1806,9 @@ const handleRequeue = async () => {
|
||||
reqData.id = selectedQueueRow.value.id
|
||||
reqData.organizationId = selectedQueueRow.value.organizationId
|
||||
} else {
|
||||
// 如果没有选中患者,使用查询条件(兼容旧逻辑)
|
||||
let orgId = selectedDept.value !== 'all' ? selectedDept.value : undefined
|
||||
// “全科”模式:优先用“当前叫号中”所在科室
|
||||
if (orgId == null) {
|
||||
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
||||
orgId = calling?.organizationId
|
||||
}
|
||||
// 如果没有选中患者,使用当前叫号中的科室
|
||||
const calling = originalQueueList.value.find((i) => i.status === '叫号中')
|
||||
const orgId = calling?.organizationId
|
||||
if (orgId != null) {
|
||||
reqData.organizationId = orgId
|
||||
}
|
||||
@@ -2121,8 +2071,6 @@ const handleTestRule = () => {
|
||||
|
||||
// 组件挂载
|
||||
onMounted(() => {
|
||||
// 加载就诊科室列表
|
||||
loadDepartmentList()
|
||||
// 初始化:优先从后端加载,失败则回退本地假数据
|
||||
loadDataFromApi()
|
||||
startWaitingTimer()
|
||||
@@ -2244,10 +2192,6 @@ onUnmounted(() => {
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.queue-actions-right {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.candidate-actions {
|
||||
@@ -2269,16 +2213,32 @@ onUnmounted(() => {
|
||||
|
||||
.filter-section {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
|
||||
.filter-label {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
.filter-left {
|
||||
flex: 1;
|
||||
|
||||
.filter-label {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.filter-button-wrapper {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.filter-select-wrapper {
|
||||
width: 100%;
|
||||
.filter-right {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
align-self: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
5
sql/bug_434_add_incision_level_to_op_schedule.sql
Normal file
5
sql/bug_434_add_incision_level_to_op_schedule.sql
Normal file
@@ -0,0 +1,5 @@
|
||||
-- Bug #434 修复:为 op_schedule 表添加 incision_level 字段
|
||||
-- 手术安排表需要存储切口类型,以便在编辑弹窗中正确回显和保存
|
||||
ALTER TABLE op_schedule ADD COLUMN IF NOT EXISTS incision_level INT2;
|
||||
|
||||
COMMENT ON COLUMN op_schedule.incision_level IS '手术切口等级 1-I级切口 2-II级切口 3-III级切口 4-IV级切口';
|
||||
18
sql/bug_462_add_specimen_code_dict.sql
Normal file
18
sql/bug_462_add_specimen_code_dict.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
-- Bug #462: 诊疗目录编辑弹窗中"所需标本"下拉框数据加载失败
|
||||
-- 根因: sys_dict_type 表中缺少 specimen_code 字典类型,sys_dict_data 表中缺少对应数据
|
||||
-- 修复: 插入字典类型及7条标本数据
|
||||
|
||||
-- 插入字典类型
|
||||
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||
VALUES ('标本类型', 'specimen_code', '0', 'admin', NOW(), '诊疗项目所需标本类型字典');
|
||||
|
||||
-- 插入标本数据
|
||||
INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, status, create_by, create_time, remark)
|
||||
VALUES
|
||||
(1, '血液', '1', 'specimen_code', '0', 'admin', NOW(), '血液标本'),
|
||||
(2, '尿液', '2', 'specimen_code', '0', 'admin', NOW(), '尿液标本'),
|
||||
(3, '粪便', '3', 'specimen_code', '0', 'admin', NOW(), '粪便标本'),
|
||||
(4, '呼吸道', '4', 'specimen_code', '0', 'admin', NOW(), '呼吸道标本'),
|
||||
(5, '无菌体液', '5', 'specimen_code', '0', 'admin', NOW(), '无菌体液标本'),
|
||||
(6, '生殖道', '6', 'specimen_code', '0', 'admin', NOW(), '生殖道标本'),
|
||||
(7, '其他', '7', 'specimen_code', '0', 'admin', NOW(), '其他标本');
|
||||
Reference in New Issue
Block a user