Merge remote-tracking branch 'origin/develop' into develop

# Conflicts:
#	openhis-ui-vue3/src/views/clinicmanagement/bargain/component/prescriptionlist.vue
This commit is contained in:
2026-04-01 18:27:31 +08:00
30 changed files with 775 additions and 594 deletions

View File

@@ -119,6 +119,7 @@ public class OutpatientChargeAppServiceImpl implements IOutpatientChargeAppServi
= outpatientChargeAppMapper.selectEncounterPatientPrescription(encounterId,
ChargeItemContext.ACTIVITY.getValue(), ChargeItemContext.MEDICATION.getValue(),
ChargeItemContext.DEVICE.getValue(), ChargeItemContext.REGISTER.getValue(),
ChargeItemContext.WESTERN_MEDICINE.getValue(), ChargeItemContext.CHINESE_PATENT_MEDICINE.getValue(),
ChargeItemStatus.PLANNED.getValue(), ChargeItemStatus.BILLABLE.getValue(),
ChargeItemStatus.BILLED.getValue(), ChargeItemStatus.REFUNDING.getValue(),
ChargeItemStatus.REFUNDED.getValue(), ChargeItemStatus.PART_REFUND.getValue(),

View File

@@ -75,8 +75,9 @@ public class OutpatientPricingAppServiceImpl implements IOutpatientPricingAppSer
}
// 门诊划价:不要强制 pricingFlag=1 参与过滤wor_activity_definition.pricing_flag 可能为 0
// 否则会导致诊疗项目(adviceType=3)查询结果为空 records=[]
String categoryCode = adviceBaseDto != null ? adviceBaseDto.getCategoryCode() : null;
return iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, searchKey, locationId, null,
organizationId, pageNo, pageSize, null, adviceTypes, null);
organizationId, pageNo, pageSize, null, adviceTypes, null, categoryCode);
}
}

View File

@@ -61,7 +61,12 @@ public class OutpatientPricingController {
@RequestParam(value = "locationId", required = false) Long locationId,
@RequestParam(value = "organizationId") Long organizationId,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(value = "categoryCode", required = false) String categoryCode) {
// 将 categoryCode 设置到 adviceBaseDto 中
if (categoryCode != null && !categoryCode.isEmpty()) {
adviceBaseDto.setCategoryCode(categoryCode);
}
return R.ok(iOutpatientPricingAppService.getAdviceBaseInfo(adviceBaseDto, searchKey, locationId, organizationId,
pageNo, pageSize));
}

View File

@@ -42,6 +42,8 @@ public interface OutpatientChargeAppMapper {
* @param medication 药品
* @param device 耗材
* @param register 挂号费
* @param westernMedicine 西药
* @param chinesePatentMedicine 中成药
* @param planned 收费状态:待收费
* @param billable 收费状态:待结算
* @param billed 收费状态:已结算
@@ -53,7 +55,9 @@ public interface OutpatientChargeAppMapper {
*/
List<EncounterPatientPrescriptionDto> selectEncounterPatientPrescription(@Param("encounterId") Long encounterId,
@Param("activity") Integer activity, @Param("medication") Integer medication, @Param("device") Integer device,
@Param("register") Integer register, @Param("planned") Integer planned, @Param("billable") Integer billable,
@Param("register") Integer register, @Param("westernMedicine") Integer westernMedicine,
@Param("chinesePatentMedicine") Integer chinesePatentMedicine,
@Param("planned") Integer planned, @Param("billable") Integer billable,
@Param("billed") Integer billed, @Param("refunding") Integer refunding, @Param("refunded") Integer refunded,
@Param("partRefund") Integer partRefund, @Param("worDeviceRequest") String worDeviceRequest);

View File

@@ -31,7 +31,7 @@ public interface IDoctorStationAdviceAppService {
*/
IPage<AdviceBaseDto> getAdviceBaseInfo(AdviceBaseDto adviceBaseDto, String searchKey, Long locationId,
List<Long> adviceDefinitionIdParamList, Long organizationId, Integer pageNo, Integer pageSize,
Integer pricingFlag, List<Integer> adviceTypes, String orderPricing);
Integer pricingFlag, List<Integer> adviceTypes, String orderPricing, String categoryCode);
/**
* 查询医嘱绑定信息

View File

@@ -19,11 +19,4 @@ public interface IDoctorStationInspectionLabApplyService {
* @return 删除结果
*/
R<?> deleteInspectionLabApply(String applyNo);
/**
* 生成检验申请单号
* 规则LS + YYYYMMDD + 5位流水号每日从1开始递增
* @return 申请单号
*/
String generateApplyNo();
}

View File

@@ -26,6 +26,7 @@ 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.openhis.medication.domain.MedicationDispense;
import com.openhis.medication.domain.MedicationRequest;
import com.openhis.medication.service.IMedicationDispenseService;
import com.openhis.medication.service.IMedicationRequestService;
@@ -44,6 +45,8 @@ import com.openhis.workflow.service.IActivityDefinitionService;
import com.openhis.workflow.service.IDeviceDispenseService;
import com.openhis.workflow.service.IDeviceRequestService;
import com.openhis.workflow.service.IServiceRequestService;
import com.openhis.workflow.domain.InventoryItem;
import com.openhis.workflow.service.IInventoryItemService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -111,6 +114,9 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
@Resource
IEncounterService iEncounterService;
@Resource
IInventoryItemService inventoryItemService;
// 缓存 key 前缀
private static final String ADVICE_BASE_INFO_CACHE_PREFIX = "advice:base:info:";
// 缓存过期时间(小时)
@@ -135,7 +141,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
@Override
public IPage<AdviceBaseDto> getAdviceBaseInfo(AdviceBaseDto adviceBaseDto, String searchKey, Long locationId,
List<Long> adviceDefinitionIdParamList, Long organizationId, Integer pageNo, Integer pageSize,
Integer pricingFlag, List<Integer> adviceTypes, String orderPricing) {
Integer pricingFlag, List<Integer> adviceTypes, String orderPricing, String categoryCode) {
// 生成缓存键处理可能的null值
String safeSearchKey = searchKey != null ? searchKey : "";
@@ -203,7 +209,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
new Page<>(pageNo, pageSize), PublicationStatus.ACTIVE.getValue(), organizationId,
CommonConstants.TableName.MED_MEDICATION_DEFINITION, CommonConstants.TableName.ADM_DEVICE_DEFINITION,
CommonConstants.TableName.WOR_ACTIVITY_DEFINITION, pricingFlag, adviceDefinitionIdParamList,
adviceTypes, searchKey,
adviceTypes, searchKey, categoryCode,
queryWrapper);
List<AdviceBaseDto> adviceBaseDtoList = adviceBaseInfo.getRecords();
@@ -932,7 +938,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
deviceAdviceDto.setAdviceTableName(CommonConstants.TableName.ADM_DEVICE_DEFINITION);
IPage<AdviceBaseDto> devicePage = getAdviceBaseInfo(deviceAdviceDto, null, null, null,
adviceSaveDto.getFounderOrgId(), 1, 1, Whether.NO.getValue(),
List.of(ItemType.DEVICE.getValue()), null);
List.of(ItemType.DEVICE.getValue()), null, null);
if (devicePage == null || devicePage.getRecords().isEmpty()) {
log.warn("无法找到耗材定价信息: deviceDefId={}", boundDevice.getDevActId());
@@ -1428,26 +1434,26 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// 只有在签发时
if (is_sign) {
// 发送跨系统申请
adviceSaveDto.setRequestId(serviceRequest.getId());
try {
// 查询诊疗定义
ActivityDefinition activityDefinition
= iActivityDefinitionService.getById(adviceSaveDto.getAdviceDefinitionId());
if (activityDefinition != null) {
// 检验 或 检查
if (ActivityType.PROOF.getValue().equals(activityDefinition.getTypeEnum())
|| ActivityType.TEST.getValue().equals(activityDefinition.getTypeEnum())) {
doctorStationSendApplyUtil.sendCrossSystemApply(adviceSaveDto, organizationId, curDate);
}
}
} catch (Exception e) {
if (!Whether.YES.getCode()
.equals(TenantOptionUtil.getOptionContent(TenantOptionDict.LIS_PACS_ERROR_IGNORE))) {
throw e;
}
log.error(e.getMessage(), e);
}
// 发送跨系统申请 - 已注释项目未使用LIS/PACS系统
// adviceSaveDto.setRequestId(serviceRequest.getId());
// try {
// // 查询诊疗定义
// ActivityDefinition activityDefinition
// = iActivityDefinitionService.getById(adviceSaveDto.getAdviceDefinitionId());
// if (activityDefinition != null) {
// // 检验 或 检查
// if (ActivityType.PROOF.getValue().equals(activityDefinition.getTypeEnum())
// || ActivityType.TEST.getValue().equals(activityDefinition.getTypeEnum())) {
// doctorStationSendApplyUtil.sendCrossSystemApply(adviceSaveDto, organizationId, curDate);
// }
// }
// } catch (Exception e) {
// if (!Whether.YES.getCode()
// .equals(TenantOptionUtil.getOptionContent(TenantOptionDict.LIS_PACS_ERROR_IGNORE))) {
// throw e;
// }
// log.error(e.getMessage(), e);
// }
}
}
}
@@ -1512,15 +1518,60 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
iChargeItemService.updatePaymentStatus(chargeItemIdList, ChargeItemStatus.DRAFT.getValue());
}
// 🔧 BugFix: 直接对所有requestId进行作废操作
log.info("BugFix#219: signOffAdvice - 作废所有请求, requestIdList={}", requestIdList);
// 🔧 新增:签退时回滚库存
// 查询已发放的药品记录(用于回滚库存)
List<MedicationDispense> dispensedList = iMedicationDispenseService.list(
new LambdaQueryWrapper<MedicationDispense>()
.in(MedicationDispense::getMedReqId, requestIdList)
.eq(MedicationDispense::getStatusEnum, DispenseStatus.COMPLETED.getValue())
);
// 尝试作废药品请求(只有存在的才会更新)
iMedicationRequestService.updateCancelledStatusBatch(requestIdList, null, null);
// 尝试作废耗材请求(只有存在的才会更新)
iDeviceRequestService.updateCancelledStatusBatch(requestIdList);
// 尝试作废诊疗请求(只有存在的才会更新)
iServiceRequestService.updateCancelledStatusBatch(requestIdList);
if (dispensedList != null && !dispensedList.isEmpty()) {
// 需要回滚的库存列表
List<InventoryItem> inventoryUpdateList = new ArrayList<>();
for (MedicationDispense dispense : dispensedList) {
// 查询对应的库存记录根据批号和药品ID
if (dispense.getMedicationId() != null && dispense.getLotNumber() != null) {
InventoryItem inventoryItem = inventoryItemService.getOne(
new LambdaQueryWrapper<InventoryItem>()
.eq(InventoryItem::getItemId, dispense.getMedicationId())
.eq(InventoryItem::getLotNumber, dispense.getLotNumber())
);
if (inventoryItem != null) {
// 计算回滚后的数量(加上已发放的数量)
BigDecimal currentQuantity = inventoryItem.getQuantity() != null ? inventoryItem.getQuantity() : BigDecimal.ZERO;
BigDecimal dispenseQuantity = dispense.getQuantity() != null ? dispense.getQuantity() : BigDecimal.ZERO;
inventoryUpdateList.add(new InventoryItem()
.setId(inventoryItem.getId())
.setQuantity(currentQuantity.add(dispenseQuantity))
);
}
}
// 更新发药记录状态为已退药
dispense.setStatusEnum(DispenseStatus.RETURNED.getValue());
}
// 批量更新库存(回滚数量)
if (!inventoryUpdateList.isEmpty()) {
inventoryItemService.updateBatchById(inventoryUpdateList);
}
// 更新发药记录状态
iMedicationDispenseService.updateBatchById(dispensedList);
}
// 🔧 BugFix: 直接对所有requestId进行签退操作将状态改为待签发
log.info("BugFix: signOffAdvice - 签退所有请求,状态改为待签发, requestIdList={}", requestIdList);
// 尝试签退药品请求(只有存在的才会更新)
iMedicationRequestService.updateDraftStatusBatch(requestIdList, null, null);
// 尝试签退耗材请求(只有存在的才会更新)
iDeviceRequestService.updateDraftStatusBatch(requestIdList);
// 尝试签退诊疗请求(只有存在的才会更新)
iServiceRequestService.updateDraftStatusBatch(requestIdList);
log.info("BugFix#219: signOffAdvice - 所有请求作废完成");

View File

@@ -369,7 +369,7 @@ public class DoctorStationChineseMedicalAppServiceImpl implements IDoctorStation
adviceBaseDto.setAdviceType(1); // 医嘱类型为药品
adviceBaseDto.setCategoryCode(MedCategoryCode.CHINESE_HERBAL_MEDICINE.getValue());// 中草药
return iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, searchKey, locationId,
adviceDefinitionIdParamList, organizationId, pageNo, pageSize, pricingFlag, List.of(1, 2, 3), null);
adviceDefinitionIdParamList, organizationId, pageNo, pageSize, pricingFlag, List.of(1, 2, 3), null, null);
}
/**
@@ -613,7 +613,7 @@ public class DoctorStationChineseMedicalAppServiceImpl implements IDoctorStation
// 对应的诊疗医嘱信息
AdviceBaseDto activityAdviceBaseDto = iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, null,
null, null, organizationId, 1, 1, Whether.NO.getValue(), List.of(3), null).getRecords().get(0);
null, null, organizationId, 1, 1, Whether.NO.getValue(), List.of(3), null, null).getRecords().get(0);
if (activityAdviceBaseDto != null) {
// 费用定价
AdvicePriceDto advicePriceDto = activityAdviceBaseDto.getPriceList().get(0);

View File

@@ -94,8 +94,39 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
* 保存检验申请单信息逻辑
* 保存检验申请单信息同时根据检验申请单检验项目数据保存检验申请单明细信息
*/
log.debug("保存检验申请单信息:{}", doctorStationLabApplyDto);
log.debug("保存申请单明细信息:{}",doctorStationLabApplyDto.getLabApplyItemList());
// 申请单号为空或"待生成"时,由后端生成新单号
String applyNo = doctorStationLabApplyDto.getApplyNo();
boolean isNewApplyNo = false;
if (applyNo == null || applyNo.trim().isEmpty() || "待生成".equals(applyNo) || "自动生成".equals(applyNo)) {
applyNo = generateApplyNo();
isNewApplyNo = true;
}
// 将生成的单号设置回 DTO
doctorStationLabApplyDto.setApplyNo(applyNo);
try {
// 执行保存逻辑
doSaveInspectionLabApply(doctorStationLabApplyDto, applyNo);
} catch (Exception e) {
// 记录废号日志(申请单号已生成但保存失败)
if (isNewApplyNo) {
log.error("申请单号 {} 因保存失败成为废号,原因:{}", applyNo, e.getMessage());
}
throw e; // 重新抛出异常,让事务回滚
}
// 返回生成的申请单号
Map<String, Object> result = new HashMap<>();
result.put("applyNo", applyNo);
return R.ok(result);
}
/**
* 执行保存检验申请单的实际逻辑
*/
private void doSaveInspectionLabApply(DoctorStationLabApplyDto doctorStationLabApplyDto, String applyNo) {
//获取当前登陆用户 ID
String userId = String.valueOf(SecurityUtils.getLoginUser().getUserId());
InspectionLabApply inspectionLabApply = new InspectionLabApply();
@@ -108,18 +139,30 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
inspectionLabApply.setOperatorId(userId);
inspectionLabApply.setCreateTime(new Date());
inspectionLabApply.setDeleteFlag(DelFlag.NO.getCode());
// 申请日期使用服务器当前系统时间
inspectionLabApply.setApplyTime(new Date());
log.debug("保存检验申请单信息:{}", inspectionLabApply);
inspectionLabApplyService.saveOrUpdate(inspectionLabApply);
// 金额校验和重算:后端重新计算金额,防止前端篡改
java.math.BigDecimal totalAmount = java.math.BigDecimal.ZERO;
int index = 0;
//遍历 doctorStationLabApplyDto.getLabApplyItemList()
int index = 0;
for (DoctorStationLabApplyItemDto doctorStationLabApplyItemDto : doctorStationLabApplyDto.getLabApplyItemList()) {
//将 dto 数据复制到 InspectionLabApplyItem 对象中
InspectionLabApplyItem inspectionLabApplyItem = new InspectionLabApplyItem();
BeanUtils.copyProperties(doctorStationLabApplyItemDto, inspectionLabApplyItem);
// 后端重新计算金额:金额 = 单价 × 数量
java.math.BigDecimal itemPrice = doctorStationLabApplyItemDto.getItemPrice();
java.math.BigDecimal itemQty = doctorStationLabApplyItemDto.getItemQty();
if (itemPrice != null && itemQty != null) {
java.math.BigDecimal calculatedAmount = itemPrice.multiply(itemQty).setScale(2, java.math.RoundingMode.HALF_UP);
inspectionLabApplyItem.setItemAmount(calculatedAmount);
totalAmount = totalAmount.add(calculatedAmount);
}
//设置从表申请单明细的申请单号
inspectionLabApplyItem.setApplyNo(doctorStationLabApplyDto.getApplyNo());
//执行科室代码,取值于检验申请单明细(前端传递的字典值)
@@ -131,7 +174,6 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
index++;
inspectionLabApplyItem.setDeleteFlag(DelFlag.NO.getCode());
log.debug("保存申请单明细信息:{}", inspectionLabApplyItem);
inspectionLabApplyItemService.saveOrUpdate(inspectionLabApplyItem);
//创建条码对象
@@ -147,8 +189,6 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
barCode.setCreateTime(new Date());
barCode.setDeleteFlag(DelFlag.NO.getCode());
log.debug("插入条码数据前barCode:{}",barCode);
inspectionLabBarCodeService.saveOrUpdate(barCode);
}
@@ -195,15 +235,12 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
);
if (organization != null) {
positionId = organization.getId();
} else {
log.warn("未找到执行科室代码对应的科室:{}", performDeptCode);
}
}
// 如果没有指定执行科室,使用当前医生所在的科室作为默认执行科室
if (positionId == null) {
positionId = SecurityUtils.getDeptId();
log.debug("检验项目未指定执行科室,使用当前科室:{}", positionId);
}
// 4. 创建医嘱保存对象
@@ -282,12 +319,7 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
adviceSaveParam.setAdviceSaveList(adviceSaveList);
// 调用门诊医嘱保存接口,创建关联的医嘱记录
try {
iDoctorStationAdviceAppService.saveAdvice(adviceSaveParam, "1"); // "1"表示保存操作
} catch (Exception e) {
throw new RuntimeException("创建关联医嘱记录失败", e);
}
return R.ok();
iDoctorStationAdviceAppService.saveAdvice(adviceSaveParam, "1"); // "1"表示保存操作
}
/**
@@ -559,8 +591,7 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
* 支持并发安全:使用 Redis 原子递增保证唯一性
* @return 申请单号
*/
@Override
public String generateApplyNo() {
private String generateApplyNo() {
// 获取当前日期
LocalDate today = LocalDate.now();
String dateStr = today.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
@@ -574,8 +605,8 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
// 使用 Redis 原子递增获取流水号(并发安全)
long sequence = redisCache.incr(redisKey, 1);
// 设置 Redis key 过期时间为 2 天,避免数据积累
redisCache.expire(redisKey, 2 * 24 * 60 * 60);
// 设置 Redis key 过期时间(每天的 key 按日期独立隔天不再使用25小时确保跨午夜场景安全
redisCache.expire(redisKey, 25 * 60 * 60);
// 格式化流水号为5位不足前补0
String sequenceStr = String.format("%05d", sequence);
@@ -583,7 +614,6 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
// 生成完整的申请单号
String applyNo = prefix + sequenceStr;
log.debug("生成检验申请单号:{}", applyNo);
return applyNo;
}

View File

@@ -52,7 +52,7 @@ public class DoctorStationAdviceController {
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
return R.ok(iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, searchKey, locationId,
adviceDefinitionIdParamList, organizationId, pageNo, pageSize, Whether.NO.getValue(), adviceTypes, null));
adviceDefinitionIdParamList, organizationId, pageNo, pageSize, Whether.NO.getValue(), adviceTypes, null, null));
}
/**

View File

@@ -64,18 +64,4 @@ public class DoctorStationInspectionLabApplyController {
log.debug("删除检验申请单:{}", applyNo);
return R.ok(iDoctorStationInspectionLabApplyService.deleteInspectionLabApply(applyNo));
}
/**
* 生成检验申请单号
* 规则LS + YYYYMMDD + 5位流水号每日从1开始递增
* @return 申请单号
*/
@GetMapping(value = "/generate-apply-no")
public R<?> generateApplyNo(){
log.debug("生成检验申请单号");
String applyNo = iDoctorStationInspectionLabApplyService.generateApplyNo();
Map<String, String> result = new HashMap<>();
result.put("applyNo", applyNo);
return R.ok(result);
}
}

View File

@@ -38,6 +38,7 @@ public interface DoctorStationAdviceAppMapper {
@Param("adviceDefinitionIdParamList") List<Long> adviceDefinitionIdParamList,
@Param("adviceTypes") List<Integer> adviceTypes,
@Param("searchKey") String searchKey,
@Param("categoryCode") String categoryCode,
@Param(Constants.WRAPPER) QueryWrapper<AdviceBaseDto> queryWrapper);
/**

View File

@@ -364,7 +364,7 @@ public class AdviceUtils {
// 对应的子项诊疗医嘱信息
AdviceBaseDto activityAdviceBaseDto
= iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, null, null, null, organizationId, 1,
1, Whether.NO.getValue(), List.of(1, 2, 3), null).getRecords().get(0);
1, Whether.NO.getValue(), List.of(1, 2, 3), null, null).getRecords().get(0);
if (activityAdviceBaseDto != null) {
// 费用定价
AdvicePriceDto advicePriceDto = activityAdviceBaseDto.getPriceList().get(0);

View File

@@ -32,9 +32,11 @@ public class PrescriptionUtils {
if (medicineList == null || medicineList.isEmpty()) {
return;
}
// 1. 按诊断ID分组不同诊断必须分开
// 1. 按诊断ID分组不同诊断必须分开null值归为一组
Map<Long, List<AdviceSaveDto>> diagnosisGroups =
medicineList.stream().collect(Collectors.groupingBy(AdviceSaveDto::getConditionDefinitionId));
medicineList.stream().collect(Collectors.groupingBy(dto ->
dto.getConditionDefinitionId() != null ? dto.getConditionDefinitionId() : 0L
));
// 2. 处理每个诊断组
diagnosisGroups.values().forEach(this::processDiagnosisGroup);
}
@@ -46,9 +48,11 @@ public class PrescriptionUtils {
if (diagnosisGroup.isEmpty()) {
return;
}
// 1. 按药品性质分组
// 1. 按药品性质分组null值归为普通药品
Map<String, List<AdviceSaveDto>> pharmacologyGroups =
diagnosisGroup.stream().collect(Collectors.groupingBy(AdviceSaveDto::getPharmacologyCategoryCode));
diagnosisGroup.stream().collect(Collectors.groupingBy(dto ->
dto.getPharmacologyCategoryCode() != null ? dto.getPharmacologyCategoryCode() : "0"
));
// 2. 处理每个药品性质组
pharmacologyGroups.values().forEach(pharmaGroup -> {
// 2.1 先处理有分组ID的药品确保它们不会被拆分

View File

@@ -702,7 +702,7 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
= medUseExeList.stream().map(MedicationRequestUseExe::getMedicationId).collect(Collectors.toList());
// 医嘱详细信息
List<AdviceBaseDto> medicationInfos = doctorStationAdviceAppService.getAdviceBaseInfo(null, null, null,
medicationDefinitionIdList, 0L, 1, 500, Whether.NO.getValue(), List.of(1), null).getRecords();
medicationDefinitionIdList, 0L, 1, 500, Whether.NO.getValue(), List.of(1), null, null).getRecords();
// 当前时间
Date curDate = new Date();
@@ -979,7 +979,7 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
= actUseExeList.stream().map(ServiceRequestUseExe::getActivityId).collect(Collectors.toList());
// 医嘱详细信息
List<AdviceBaseDto> activityInfos = doctorStationAdviceAppService.getAdviceBaseInfo(null, null, null,
activityDefinitionIdList, 0L, 1, 500, Whether.NO.getValue(), List.of(3), null).getRecords();
activityDefinitionIdList, 0L, 1, 500, Whether.NO.getValue(), List.of(3), null, null).getRecords();
// 当前时间
Date curDate = new Date();
@@ -1146,7 +1146,7 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
// 耗材医嘱详细信息
List<AdviceBaseDto> deviceInfos = doctorStationAdviceAppService
.getAdviceBaseInfo(null, null, null, deviceIds, 0L, 1, 500, Whether.NO.getValue(), List.of(2), null)
.getAdviceBaseInfo(null, null, null, deviceIds, 0L, 1, 500, Whether.NO.getValue(), List.of(2), null, null)
.getRecords();
DeviceRequest deviceRequest;

View File

@@ -201,7 +201,7 @@ public class EncounterAutoRollAppServiceImpl implements IEncounterAutoRollAppSer
.map(AutoRollNursingDto::getActivityDefinitionId).collect(Collectors.toList());
// 诊疗医嘱信息
List<AdviceBaseDto> activityInfos = doctorStationAdviceAppService.getAdviceBaseInfo(null, null, null,
activityDefinitionIdList, 0L, 1, 500, Whether.NO.getValue(), List.of(3), orderPricing).getRecords();
activityDefinitionIdList, 0L, 1, 500, Whether.NO.getValue(), List.of(3), orderPricing, null).getRecords();
// 计费
ChargeItem chargeItem;
@@ -295,7 +295,7 @@ public class EncounterAutoRollAppServiceImpl implements IEncounterAutoRollAppSer
.map(AutoRollBasicServiceDto::getActivityDefinitionId).collect(Collectors.toList());
// 诊疗医嘱信息
List<AdviceBaseDto> activityInfos = doctorStationAdviceAppService.getAdviceBaseInfo(null, null, null,
activityDefinitionIdList, 0L, 1, 500, Whether.NO.getValue(), List.of(3), orderPricing).getRecords();
activityDefinitionIdList, 0L, 1, 500, Whether.NO.getValue(), List.of(3), orderPricing, null).getRecords();
// 计费
ChargeItem chargeItem;
for (AutoRollBasicServiceDto autoRollBasicServiceDto : autoRollBasicService) {

View File

@@ -2095,7 +2095,7 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
adviceBaseDto.setAdviceDefinitionId(activityDeviceDto.getDevActId());
// 对应的诊疗医嘱信息
AdviceBaseDto activityAdviceBaseDto = iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, null,
null, null, organizationId, 1, 1, Whether.NO.getValue(), List.of(1, 2, 3), null).getRecords().get(0);
null, null, organizationId, 1, 1, Whether.NO.getValue(), List.of(1, 2, 3), null, null).getRecords().get(0);
// 价格信息
if (activityAdviceBaseDto != null) {
// 费用定价

View File

@@ -197,7 +197,7 @@ public class OrdersGroupPackageAppServiceImpl implements IOrdersGroupPackageAppS
// 医嘱下拉详细信息
List<AdviceBaseDto> personalRecords =
iDoctorStationAdviceAppService.getAdviceBaseInfo(null, null, null, orderDefinitionIdParamList,
organizationId, 1, 100, Whether.NO.getValue(), List.of(1, 2, 3), null).getRecords();
organizationId, 1, 100, Whether.NO.getValue(), List.of(1, 2, 3), null, null).getRecords();
// 创建AdviceBaseDto的映射以adviceDefinitionId为key
Map<Long, AdviceBaseDto> adviceMap = personalRecords.stream().collect(Collectors
.toMap(AdviceBaseDto::getAdviceDefinitionId, advice -> advice, (existing, replacement) -> existing // 如果有重复key保留第一个
@@ -248,7 +248,7 @@ public class OrdersGroupPackageAppServiceImpl implements IOrdersGroupPackageAppS
// 医嘱下拉详细信息
List<AdviceBaseDto> personalRecords =
iDoctorStationAdviceAppService.getAdviceBaseInfo(null, null, null, orderDefinitionIdParamList,
organizationId, 1, 100, Whether.NO.getValue(), List.of(1, 2, 3), null).getRecords();
organizationId, 1, 100, Whether.NO.getValue(), List.of(1, 2, 3), null, null).getRecords();
// 创建AdviceBaseDto的映射以adviceDefinitionId为key
Map<Long, AdviceBaseDto> adviceMap = personalRecords.stream().collect(Collectors
.toMap(AdviceBaseDto::getAdviceDefinitionId, advice -> advice, (existing, replacement) -> existing // 如果有重复key保留第一个
@@ -297,7 +297,7 @@ public class OrdersGroupPackageAppServiceImpl implements IOrdersGroupPackageAppS
// 医嘱下拉详细信息
List<AdviceBaseDto> personalRecords =
iDoctorStationAdviceAppService.getAdviceBaseInfo(null, null, null, orderDefinitionIdParamList,
organizationId, 1, 100, Whether.NO.getValue(), List.of(1, 2, 3), null).getRecords();
organizationId, 1, 100, Whether.NO.getValue(), List.of(1, 2, 3), null, null).getRecords();
// 创建AdviceBaseDto的映射以adviceDefinitionId为key
Map<Long, AdviceBaseDto> adviceMap = personalRecords.stream().collect(Collectors
.toMap(AdviceBaseDto::getAdviceDefinitionId, advice -> advice, (existing, replacement) -> existing // 如果有重复key保留第一个

View File

@@ -161,7 +161,7 @@ public class SpecialAdviceAppServiceImpl implements ISpecialAdviceAppService {
adviceBaseDto.setAdviceDefinitionId(definitionId); // 医嘱定义id
// 对应的诊疗医嘱信息
activityAdviceBaseDto = iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, null, null,
null, null, 1, 1, Whether.NO.getValue(), List.of(3), null).getRecords().get(0);
null, null, 1, 1, Whether.NO.getValue(), List.of(3), null, null).getRecords().get(0);
// 逻辑1---------------------直接新增
longServiceRequest.setStatusEnum(RequestStatus.DRAFT.getValue());// 请求状态
longServiceRequest.setOccurrenceStartTime(startTime); // 医嘱开始时间
@@ -208,7 +208,7 @@ public class SpecialAdviceAppServiceImpl implements ISpecialAdviceAppService {
adviceBaseDto.setAdviceDefinitionId(definitionId); // 医嘱定义id
// 对应的诊疗医嘱信息
activityAdviceBaseDto = iDoctorStationAdviceAppService
.getAdviceBaseInfo(adviceBaseDto, null, null, null, null, 1, 1, Whether.NO.getValue(), List.of(3), null)
.getAdviceBaseInfo(adviceBaseDto, null, null, null, null, 1, 1, Whether.NO.getValue(), List.of(3), null, null)
.getRecords().get(0);
longServiceRequest.setStatusEnum(RequestStatus.DRAFT.getValue());// 请求状态
@@ -348,7 +348,7 @@ public class SpecialAdviceAppServiceImpl implements ISpecialAdviceAppService {
adviceBaseDto.setAdviceDefinitionId(transferOrganizationDefinitionId); // 医嘱定义id
// 转科的医嘱信息
AdviceBaseDto activityAdviceBaseDto = iDoctorStationAdviceAppService
.getAdviceBaseInfo(adviceBaseDto, null, null, null, null, 1, 1, Whether.NO.getValue(), List.of(3), null)
.getAdviceBaseInfo(adviceBaseDto, null, null, null, null, 1, 1, Whether.NO.getValue(), List.of(3), null, null)
.getRecords().get(0);
// 保存转科医嘱请求
ServiceRequest serviceRequest = new ServiceRequest();
@@ -430,7 +430,7 @@ public class SpecialAdviceAppServiceImpl implements ISpecialAdviceAppService {
// 出院的医嘱信息
AdviceBaseDto activityAdviceBaseDto = iDoctorStationAdviceAppService.getAdviceBaseInfo(null, null, null,
List.of(transferOrganizationDefinitionId), null, 1, 1, Whether.NO.getValue(), List.of(3), null).getRecords()
List.of(transferOrganizationDefinitionId), null, 1, 1, Whether.NO.getValue(), List.of(3), null, null).getRecords()
.get(0);
// 保存出院医嘱请求
ServiceRequest serviceRequest = new ServiceRequest();

View File

@@ -101,6 +101,9 @@
<if test="pricingFlag == 1">
AND 1 = 2
</if>
<if test="categoryCode != null and categoryCode != ''">
AND t1.category_code = #{categoryCode}
</if>
<if test="searchKey != null and searchKey != ''">
AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%')
</if>

View File

@@ -31,10 +31,20 @@ public enum ChargeItemContext implements HisEnumInterface {
*/
ACTIVITY(3, "3", "项目"),
/**
* 西药
*/
WESTERN_MEDICINE(5, "5", "西药"),
/**
* 中成药
*/
CHINESE_PATENT_MEDICINE(6, "6", "中成药"),
/**
* 挂号
*/
REGISTER(4, "4", "挂号");
REGISTER(7, "7", "挂号");
private final Integer value;
private final String code;

View File

@@ -86,7 +86,12 @@ public enum DispenseStatus implements HisEnumInterface {
/**
* 待退药
*/
PENDING_REFUND(16, "PRD", "待退药");
PENDING_REFUND(16, "PRD", "待退药"),
/**
* 已退药
*/
RETURNED(17, "RT", "已退药");
private Integer value;
private String code;

View File

@@ -53,17 +53,20 @@ const currentSelectRow = ref({});
const queryParams = ref({
pageSize: 100,
pageNum: 1,
adviceType: undefined,
categoryCode: '',
});
const adviceBaseList = ref([]);
// 节流函数
const throttledGetList = throttle(
() => {
// 触发数据加载
getList();
},
300,
{ leading: true, trailing: true }
);
watch(
watch(
() => props.adviceQueryParams,
(newValue) => {
// 只有在弹窗打开时才响应 adviceQueryParams 的变化,避免选择项目后弹窗关闭时触发不必要的请求
@@ -72,6 +75,7 @@ watch(
}
queryParams.value.searchKey = newValue?.searchKey;
queryParams.value.adviceType = newValue?.adviceType;
queryParams.value.categoryCode = newValue?.categoryCode;
throttledGetList();
},
{ deep: true }
@@ -86,6 +90,11 @@ watch(
if (props.adviceQueryParams) {
queryParams.value.searchKey = props.adviceQueryParams.searchKey;
queryParams.value.adviceType = props.adviceQueryParams.adviceType;
queryParams.value.categoryCode = props.adviceQueryParams.categoryCode;
console.log('[adviceBaseList] 弹窗打开,参数:', JSON.stringify({
adviceType: queryParams.value.adviceType,
categoryCode: queryParams.value.categoryCode
}));
}
// 主动触发数据加载
getList();

View File

@@ -18,28 +18,29 @@
@row-dblclick="clickRowDb"
:expand-row-keys="expandOrder"
>
<el-table-column type="expand" width="1" style="width: 0">
<el-table-column type="expand" width="40">
<template #default="scope">
<el-form :model="scope.row" :rules="rowRules" :ref="'formRef' + scope.$index">
<div style="padding: 16px; background: #f8f9fa; border-radius: 8px">
<template v-if="scope.row.adviceType == 2">
<!-- 药品类型adviceType == 1和耗材类型adviceType == 2使用相同的界面 -->
<template v-if="scope.row.adviceType == 1 || scope.row.adviceType == 2">
<div style="display: flex; align-items: center; margin-bottom: 16px; gap: 16px">
<span style="font-size: 16px; font-weight: 600">
{{
scope.row.adviceName +
' ' +
scope.row.volume +
' ' +
scope.row.unitPrice +
' 元/' +
scope.row.unitCode_dictText
(scope.row.volume ? scope.row.volume + ' ' : '') +
(scope.row.unitPrice ? scope.row.unitPrice + ' 元/' : '') +
(scope.row.unitCode_dictText || '')
}}
</span>
<div class="form-group">
<!-- 库存不为空时显示批号选择 -->
<el-select
v-if="scope.row.stockList && scope.row.stockList.length > 0"
v-model="scope.row.lotNumber"
style="width: 180px; margin-right: 20px"
placeholder="药房"
placeholder="选择批号"
>
<el-option
v-for="item in scope.row.stockList"
@@ -52,7 +53,7 @@
item.lotNumber +
' ' +
' 库存:' +
item.quantity / scope.row.partPercent +
(item.quantity / scope.row.partPercent).toFixed(2) +
item.unitCode_dictText +
' 单价:' +
item.price.toFixed(2) +
@@ -62,6 +63,10 @@
@click="handleNumberClick(item, scope.$index)"
/>
</el-select>
<!-- 库存为空时显示提示 -->
<span v-else style="color: #f56c6c; margin-right: 20px; font-size: 14px;">
无可用库存
</span>
<el-form-item
label="数量:"
prop="quantity"
@@ -79,9 +84,10 @@
/>
</el-form-item>
<el-select
v-if="scope.row.unitCodeList && scope.row.unitCodeList.length > 0"
v-model="scope.row.unitCode"
style="width: 70px; margin-right: 20px"
placeholder=" "
placeholder="单位"
@change="calculateTotalAmount(scope.row, scope.$index)"
>
<template v-for="item in scope.row.unitCodeList" :key="item.value">
@@ -157,7 +163,7 @@
<el-table-column label="" align="center" prop="groupId" width="60">
<template #default="scope">
<el-checkbox
:disabled = "scope.row.bizRequestFlag==0"
:disabled = "scope.row.chargeStatus == 5"
v-model="scope.row.check"
placeholder=""
@click.stop=""
@@ -177,13 +183,60 @@
<template v-if="getRowDisabled(scope.row)">
<el-select
style="width: 35%; margin-right: 20px"
v-model="scope.row.adviceType"
v-model="scope.row.adviceTypeValue"
:ref="'adviceTypeRef' + scope.$index"
placeholder="选择类型"
@change="
(value) => {
console.log('[类型选择] value:', value);
expandOrder = [];
prescriptionList[scope.$index].adviceName = undefined;
adviceQueryParams.adviceType = value;
// 根据 value 值直接判断
let adviceType, categoryCode, label;
switch (value) {
case '1':
adviceType = 1;
categoryCode = '2';
label = '西药';
break;
case '2':
adviceType = 1;
categoryCode = '1';
label = '中成药';
break;
case '3':
adviceType = 2;
categoryCode = '';
label = '耗材';
break;
case '4':
adviceType = 3;
categoryCode = '';
label = '诊疗';
break;
default:
adviceType = undefined;
categoryCode = '';
label = '';
}
prescriptionList[scope.$index].adviceType = adviceType;
prescriptionList[scope.$index].adviceType_dictText = label;
prescriptionList[scope.$index].categoryCode = categoryCode;
adviceQueryParams.adviceType = adviceType;
adviceQueryParams.categoryCode = categoryCode;
console.log('[类型选择] 设置后:', { adviceType, categoryCode });
}
"
@clear="
() => {
prescriptionList[scope.$index].adviceName = undefined;
prescriptionList[scope.$index].adviceType = undefined;
prescriptionList[scope.$index].adviceType_dictText = '';
prescriptionList[scope.$index].categoryCode = '';
adviceQueryParams.adviceType = undefined;
adviceQueryParams.categoryCode = '';
}
"
>
@@ -192,12 +245,6 @@
:key="item.value"
:label="item.label"
:value="item.value"
@click="
() => {
prescriptionList[scope.$index].adviceType = item.value;
prescriptionList[scope.$index].adviceType_dictText = item.label;
}
"
/>
</el-select>
<el-popover
@@ -243,7 +290,8 @@
</el-table-column>
<el-table-column label="状态" align="center" prop="" width="90">
<template #default="scope">
<el-tag v-if="scope.row.statusEnum == 2" type="success">签发</el-tag>
<el-tag v-if="scope.row.chargeStatus == 5" type="success">收费</el-tag>
<el-tag v-else-if="scope.row.statusEnum == 2" type="success">已签发</el-tag>
<el-tag v-else-if="scope.row.statusEnum == 1" type="">待签发</el-tag>
</template>
</el-table-column>
@@ -289,7 +337,7 @@
<script setup>
import {getOrgTree, getPrescriptionList, savePrescription, savePrescriptionSign, singOut,} from './api';
import adviceBaseList from './adviceBaseList';
import {computed, getCurrentInstance, nextTick, ref, watch} from 'vue';
import {getCurrentInstance, nextTick, ref, watch} from 'vue';
const emit = defineEmits(['selectDiagnosis']);
const prescriptionList = ref([]);
@@ -334,35 +382,46 @@ const groupList = ref([])
const { proxy } = getCurrentInstance();
const inputRefs = ref({}); // 存储输入框实例
const requiredProps = ref([]); // 存储必填项 prop 顺序
const { method_code, unit_code, rate_code, distribution_category_code, drord_doctor_type } = proxy.useDict(
const { method_code, unit_code, rate_code, distribution_category_code } = proxy.useDict(
'method_code',
'unit_code',
'rate_code',
'distribution_category_code',
'drord_doctor_type'
'distribution_category_code'
);
const handleSaveDisabled = ref(false) //签发状态
const handleSingOutDisabled = ref(false) //签退状态
// 使用 drord_doctor_type 字典
const adviceTypeList = computed(() => {
if (drord_doctor_type.value && drord_doctor_type.value.length > 0) {
// 只保留耗材(4)和诊疗(3)类型,并添加全部选项
const filtered = drord_doctor_type.value.filter(item => {
const val = parseInt(item.value);
return val === 3 || val === 4; // 诊疗和耗材
}).map(item => ({
label: item.label,
value: parseInt(item.value)
}));
return [...filtered, { label: '全部', value: undefined }];
}
// 默认值
return [
{ label: '耗材', value: 4 },
{ label: '诊疗', value: 3 },
{ label: '全部', value: undefined },
];
});
const adviceTypeList = ref([
{
label: '西药',
value: '1', // 用字符串
adviceType: 1,
categoryCode: '2',
},
{
label: '中成药',
value: '2', // 用字符串
adviceType: 1,
categoryCode: '1',
},
{
label: '耗材',
value: '3', // 用字符串
adviceType: 2,
categoryCode: '',
},
{
label: '诊疗',
value: '4', // 用字符串
adviceType: 3,
categoryCode: '',
},
{
label: '全部',
value: '',
adviceType: undefined,
categoryCode: '',
},
]);
watch(
() => expandOrder.value,
(newValue) => {
@@ -386,9 +445,6 @@ watch(
if(newValue&&newValue.length>0){
let saveList = prescriptionList.value.filter((item) => {
return item.statusEnum == 1&&(Number(item.bizRequestFlag)==1||!item.bizRequestFlag)
})
prescriptionList.value.map(k=>{
k.check = false
})
console.log(saveList,"prescriptionList.value")
if (saveList.length == 0) {
@@ -398,13 +454,30 @@ watch(
}
}
},
{ immediate: true }
{ immediate: true, deep: false }
);
function getListInfo(addNewRow) {
isAdding.value = false;
getPrescriptionList(props.patientInfo.encounterId).then((res) => {
prescriptionList.value = res.data;
// 为每行数据添加 adviceTypeValue 字段,用于类型下拉框显示
prescriptionList.value = (res.data || []).map(item => {
// 根据 adviceType 和 categoryCode 找到对应的 adviceTypeValue
let adviceTypeValue = '';
if (item.adviceType === 1) {
// 药品类型,需要根据 categoryCode 判断是西药还是中成药
if (item.categoryCode === '1') {
adviceTypeValue = '2'; // 中成药
} else {
adviceTypeValue = '1'; // 西药
}
} else if (item.adviceType === 2) {
adviceTypeValue = '3'; // 耗材
} else if (item.adviceType === 3) {
adviceTypeValue = '4'; // 诊疗
}
return { ...item, adviceTypeValue };
});
if (props.activeTab == 'prescription' && addNewRow) {
handleAddPrescription();
}
@@ -435,6 +508,10 @@ function handleAddPrescription() {
check: false,
isEdit: true,
statusEnum: 1,
adviceTypeValue: '',
adviceType: undefined,
adviceType_dictText: '',
categoryCode: '',
});
nextTick(() => {
proxy.$refs['adviceRef0'].focus();
@@ -464,10 +541,9 @@ function handleFocus(row, index) {
prescriptionList.value.forEach((r, i) => {
if (i !== index) r.showPopover = false;
});
// 如果当前行已选择adviceType同步到adviceQueryParams
if (row.adviceType !== undefined) {
adviceQueryParams.value.adviceType = row.adviceType;
}
// 同步当前行的参数到 adviceQueryParams
adviceQueryParams.value.adviceType = row.adviceType;
adviceQueryParams.value.categoryCode = row.categoryCode || '';
row.showPopover = true;
}
@@ -564,32 +640,67 @@ async function selectAdviceBase(key, row) {
// 库存列表 + 价格列表拼成批次号的下拉框(非诊疗)
if (row.adviceType != 3) {
if (row.inventoryList && row.inventoryList.length == 0) {
expandOrder.value = [];
proxy.$modal.msgWarning('该项目无库存');
return;
}
stockList.value = row.inventoryList.map((item, index) => {
return { ...item, ...row.priceList[index] };
});
prescriptionList.value[rowIndex.value].stockList = stockList.value;
// 获取默认批次号的库存,如果没有让医生重新选
let stock = stockList.value.filter((item) => {
return item.lotNumber == row.defaultLotNumber;
})[0];
if (stock != {} && stock != undefined) {
if (stock.quantity <= 0) {
proxy.$modal.msgWarning('该项目库存不足,请选择其库房');
// return;
let hasInventory = false;
let inventoryWarning = '';
// 检查库存情况
if (row.inventoryList && row.inventoryList.length > 0) {
stockList.value = row.inventoryList.map((item, index) => {
return { ...item, ...row.priceList[index] };
});
prescriptionList.value[rowIndex.value].stockList = stockList.value;
// 检查是否有可用的库存(数量 > 0
const availableStock = stockList.value.filter(item => item.quantity > 0);
if (availableStock.length > 0) {
hasInventory = true;
} else {
inventoryWarning = '该项目所有批次库存不足,请选择其库房或补充库存';
}
prescriptionList.value[rowIndex.value].lotNumber = stock.lotNumber;
prescriptionList.value[rowIndex.value].inventoryId = stock.inventoryId;
prescriptionList.value[rowIndex.value].locationId = stock.locationId;
prescriptionList.value[rowIndex.value].unitPrice = stock.price;
prescriptionList.value[rowIndex.value].positionName = stock.locationName;
// 设置默认数量为1并计算总金额
prescriptionList.value[rowIndex.value].quantity = 1;
calculateTotalPrice(prescriptionList.value[rowIndex.value], rowIndex.value);
// 获取默认批次号的库存
let stock = stockList.value.filter((item) => {
return item.lotNumber == row.defaultLotNumber;
})[0];
if (stock != {} && stock != undefined) {
if (stock.quantity > 0) {
prescriptionList.value[rowIndex.value].lotNumber = stock.lotNumber;
prescriptionList.value[rowIndex.value].inventoryId = stock.inventoryId;
prescriptionList.value[rowIndex.value].locationId = stock.locationId;
prescriptionList.value[rowIndex.value].unitPrice = stock.price;
prescriptionList.value[rowIndex.value].positionName = stock.locationName;
} else {
// 默认批次库存不足,选择第一个可用批次
const firstAvailable = availableStock[0];
if (firstAvailable) {
prescriptionList.value[rowIndex.value].lotNumber = firstAvailable.lotNumber;
prescriptionList.value[rowIndex.value].inventoryId = firstAvailable.inventoryId;
prescriptionList.value[rowIndex.value].locationId = firstAvailable.locationId;
prescriptionList.value[rowIndex.value].unitPrice = firstAvailable.price;
prescriptionList.value[rowIndex.value].positionName = firstAvailable.locationName;
}
}
}
} else {
inventoryWarning = '该项目无库存记录,请选择其他库房或补充库存';
prescriptionList.value[rowIndex.value].stockList = [];
}
// 统一设置默认值
prescriptionList.value[rowIndex.value].quantity = 1;
if (row.priceList && row.priceList.length > 0) {
if (!prescriptionList.value[rowIndex.value].unitPrice) {
prescriptionList.value[rowIndex.value].unitPrice = row.priceList[0].price;
}
}
calculateTotalPrice(prescriptionList.value[rowIndex.value], rowIndex.value);
// 如果有库存警告,统一提示
if (inventoryWarning) {
console.log('[库存警告]', inventoryWarning, '药品:', row.adviceName);
// 不弹出警告框,只在控制台记录,避免频繁打扰用户
// 用户可以在保存时看到真正的库存检查结果
}
} else {
// 诊疗:设置执行科室和价格
@@ -667,41 +778,67 @@ function ensureOrgTreeLoaded() {
}
function handleDelete() {
let deleteList = prescriptionList.value
.filter((item) => {
return item.check && item.statusEnum == 1;
})
.map((item) => {
return {
requestId: item.requestId,
dbOpType: '3',
adviceType: item.adviceType,
};
});
if (deleteList.length == 0) {
// 🔧 修复:使用 groupIndexList 而不是 check 属性
// 因为 watch 监听器会在数据更新时重置 check 为 false
if (groupIndexList.value.length == 0) {
proxy.$modal.msgWarning('请选择要删除的项目');
return;
}
if (!deleteList[0].requestId) {
prescriptionList.value.shift();
} else {
let deleteList = groupIndexList.value.map((index) => {
const item = prescriptionList.value[index];
// 只删除待签发且未收费的项目
if (item.statusEnum != 1 || item.chargeStatus == 5) {
return null;
}
return {
requestId: item.requestId,
dbOpType: '3',
adviceType: item.adviceType,
};
}).filter(item => item !== null); // 过滤掉已签发或已收费的项目
if (deleteList.length == 0) {
proxy.$modal.msgWarning('只能删除待签发且未收费的项目');
return;
}
// 删除逻辑:按索引从大到小排序,避免删除后索引变化
const sortedIndexes = groupIndexList.value.sort((a, b) => b - a);
let hasSavedItem = false;
for (const index of sortedIndexes) {
const item = prescriptionList.value[index];
if (item.statusEnum != 1) {
continue; // 跳过已签发的项目
}
if (!item.requestId) {
// 新增的行(未保存到数据库),直接删除
prescriptionList.value.splice(index, 1);
} else {
hasSavedItem = true;
}
}
if (hasSavedItem) {
// 有已保存的行调用后端API删除
savePrescription({ adviceSaveList: deleteList }).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('操作成功');
getListInfo(false);
}
});
} else {
// 只有新增行,已经在前端删除完成
proxy.$modal.msgSuccess('操作成功');
}
// groupIndexList.value
// .sort((a, b) => b - a)
// .forEach((item) => {
// prescriptionList.value.splice(item, 1);
// });
// groupIndexList.value = [];
expandOrder.value = [];
groupIndexList.value = [];
groupList.value = [];
isAdding.value = false;
adviceQueryParams.value.adviceType = undefined;
// prescriptionList.value.splice(index, 1);
}
function handleNumberClick(item, index) {
@@ -713,11 +850,21 @@ function handleNumberClick(item, index) {
}
function changeCheck(value,index,row){
if (value) {
groupIndexList.value.push(index)
groupList.value.push(row)
if (groupIndexList.value.indexOf(index) === -1) {
groupIndexList.value.push(index)
}
if (groupList.value.indexOf(row) === -1) {
groupList.value.push(row)
}
} else {
groupIndexList.value.splice(groupIndexList.value.indexOf(index), 1)
groupList.value.splice(groupList.value.indexOf(index), 1)
const idx1 = groupIndexList.value.indexOf(index)
if (idx1 !== -1) {
groupIndexList.value.splice(idx1, 1)
}
const idx2 = groupList.value.indexOf(row)
if (idx2 !== -1) {
groupList.value.splice(idx2, 1)
}
}
groupList.value.map(k=>{
if(k.check){
@@ -851,14 +998,14 @@ function handleSingOut() {
return item.check;
})
.filter((item) => {
return item.statusEnum == 2&&(Number(item.bizRequestFlag)==1||!item.bizRequestFlag)
return item.statusEnum == 2 && item.chargeStatus != 5 && (Number(item.bizRequestFlag)==1||!item.bizRequestFlag)
})
.map((item) => {
return item.requestId;
});
console.log(requestIdList,"签退")
if (requestIdList.length == 0) {
proxy.$modal.msgWarning('未选择可签退的医嘱');
proxy.$modal.msgWarning('未选择可签退的医嘱(已收费项目不可签退)');
return
}
singOut(requestIdList).then((res) => {

View File

@@ -143,8 +143,8 @@ watch(
() => props.adviceQueryParams,
(newValue) => {
queryParams.value.searchKey = newValue.searchKey;
if (newValue.adviceType) {
queryParams.value.adviceTypes = [newValue.adviceType].join(',');
if (newValue.adviceTypes) {
queryParams.value.adviceTypes = [newValue.adviceTypes].join(',');
} else {
queryParams.value.adviceTypes = '1,2,3';
}

View File

@@ -1003,18 +1003,6 @@ export function deleteInspectionApplication(applyNo) {
});
}
/**
* 生成检验申请单号
* 规则LS + YYYYMMDD + 5位流水号每日从1开始递增
* @returns {Promise} { code: 200, data: { applyNo: string } }
*/
export function generateInspectionApplyNo() {
return request({
url: '/doctor-station/inspection/generate-apply-no',
method: 'get',
});
}
/**
* 分页获取检验类型列表(分类)
* @param {Object} queryParams - 查询参数

View File

@@ -108,7 +108,7 @@
<el-tab-pane label="申请单" name="application">
<el-form class="application-form" :model="formData" label-width="auto">
<el-form-item label="申请单号" style="margin-bottom: 1px">
<el-input v-model="formData.applyNo" readonly size="small" />
<el-input v-model="formData.applyNo" disabled size="small" />
</el-form-item>
<!-- 患者信息行 -->
@@ -141,14 +141,10 @@
<!--申请日期-->
<el-col :span="8">
<el-form-item label="申请日期" required>
<el-date-picker
<el-input
v-model="formData.applyTime"
type="datetime"
placeholder="选择日期时间"
readonly
size="small"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 100%"
/>
</el-form-item>
</el-col>
@@ -539,17 +535,15 @@
</template>
<script setup>
import {onMounted, reactive, ref, watch, computed, getCurrentInstance} from 'vue'
import {onMounted, onUnmounted, reactive, ref, watch, computed, getCurrentInstance} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import { DocumentChecked, Plus, Document, Printer, Delete, Check, Loading } from '@element-plus/icons-vue'
import {
checkInspectionApplicationNo,
deleteInspectionApplication, getApplyList,
saveInspectionApplication,
getInspectionTypeList,
getInspectionItemList,
getEncounterDiagnosis,
generateInspectionApplyNo,
getInspectionApplyDetail
} from '../api'
import useUserStore from '@/store/modules/user.js'
@@ -588,14 +582,16 @@ const loading = ref(false)
const saving = ref(false) // 保存状态
const total = ref(0)
const leftActiveTab = ref('application')
const isGeneratingNewApplyNo = ref(false) // 标志:是否正在生成新申请单号
// 申请日期实时更新定时器
let applyTimeTimer = null
// 用户信息store
const userStore = useUserStore()
const { id: userId, name: userName, nickName: userNickName } = storeToRefs(userStore)
// 修改 initData 函数
async function initData() {
const initData = async () => {
// 先初始化患者信息(如果有)
if (props.patientInfo && props.patientInfo.encounterId) {
queryParams.encounterId = props.patientInfo.encounterId
@@ -606,15 +602,16 @@ async function initData() {
formData.applyDepartment = props.patientInfo.organizationName || ''
formData.applyDocName = userNickName.value || userName.value || ''
formData.applyDocCode = userId.value || ''
//此处样本数据暂时固定为血液,后续根据实际情况调整
formData.specimenName = '血液'
formData.applyDeptCode = props.patientInfo.organizationName || ''
formData.applyOrganizationId = props.patientInfo.orgId || ''
formData.encounterId = props.patientInfo.encounterId
// 生成申请单号
generateApplicationNo().then((newApplyNo) => {
formData.applyNo = newApplyNo
})
// 申请单号在保存时由后端生成,此处显示"自动生成"
formData.applyNo = '自动生成'
// 申请日期实时更新(启动定时器)
startApplyTimeTimer()
// 获取主诊断信息
try {
@@ -657,7 +654,7 @@ const formData = reactive({
patientName: '',
medicalrecordNumber: '',
natureofCost: 'self',
applyTime: new Date(),
applyTime: '', // 初始值设为空字符串,在新增时设置为当前时间
applyDepartment: '',
applyDocName: '',
executeDepartment: '',
@@ -728,7 +725,7 @@ const PAGE_SIZE = 50
const SEARCH_DEBOUNCE_TIME = 300
// 加载检验类型分类列表(只加载分类,项目懒加载)
async function loadInspectionData() {
const loadInspectionData = async () => {
// 如果已经加载过分类,直接返回
if (inspectionCategories.value.length > 0) {
return
@@ -776,7 +773,7 @@ async function loadInspectionData() {
}
// 懒加载分类项目(分页)
async function loadCategoryItems(categoryKey, loadMore = false) {
const loadCategoryItems = async (categoryKey, loadMore = false) => {
const category = inspectionCategories.value.find(c => c.key === categoryKey)
if (!category) return
@@ -860,7 +857,7 @@ async function loadCategoryItems(categoryKey, loadMore = false) {
}
// 加载更多项目
function loadMoreItems(categoryKey) {
const loadMoreItems = (categoryKey) => {
const category = inspectionCategories.value.find(c => c.key === categoryKey)
if (!category || !category.hasMore || category.loading) return
@@ -869,7 +866,7 @@ function loadMoreItems(categoryKey) {
}
// 处理滚动事件(无限滚动)
function handleScroll({ scrollTop, scrollHeight, clientHeight }) {
const handleScroll = ({ scrollTop, scrollHeight, clientHeight }) => {
// 距离底部 50px 时触发加载更多
if (scrollHeight - scrollTop - clientHeight < 50) {
const expandedCategory = inspectionCategories.value.find(c => c.expanded)
@@ -916,7 +913,7 @@ const getFilteredItems = (categoryKey) => {
}
// 搜索建议查询(自动完成)
async function querySearchInspectionItems(queryString, cb) {
const querySearchInspectionItems = async (queryString, cb) => {
if (!queryString) {
cb([])
return
@@ -954,7 +951,7 @@ async function querySearchInspectionItems(queryString, cb) {
}
// 搜索选择处理
function handleSearchSelect(item) {
const handleSearchSelect = (item) => {
// 直接添加到已选列表
if (!isItemSelected(item)) {
selectedInspectionItems.value.push({
@@ -967,12 +964,12 @@ function handleSearchSelect(item) {
}
// 搜索框清空处理
function handleSearchClear() {
const handleSearchClear = () => {
searchKeyword.value = ''
}
// 获取检验申请单列表
function getInspectionList() {
const getInspectionList = () => {
// 如果没有encounterId,不调用接口
if (!queryParams.encounterId) {
return
@@ -1024,7 +1021,7 @@ function getInspectionList() {
}
// 合并检验申请单记录:将同一个申请单的多个明细合并成一条记录
function mergeInspectionApplyRecords(records) {
const mergeInspectionApplyRecords = (records) => {
if (!records || records.length === 0) {
return []
}
@@ -1052,7 +1049,7 @@ function mergeInspectionApplyRecords(records) {
}
// 格式化金额:确保显示两位小数
function formatAmount(amount) {
const formatAmount = (amount) => {
if (amount === null || amount === undefined || amount === '') {
return '0.00'
}
@@ -1063,53 +1060,60 @@ function formatAmount(amount) {
return num.toFixed(2)
}
// 格式化日期时间为字符串 YYYY-MM-DD HH:mm:ss
const formatDateTime = (date) => {
if (!date) return ''
const d = new Date(date)
const year = d.getFullYear()
const month = String(d.getMonth() + 1).padStart(2, '0')
const day = String(d.getDate()).padStart(2, '0')
const hours = String(d.getHours()).padStart(2, '0')
const minutes = String(d.getMinutes()).padStart(2, '0')
const seconds = String(d.getSeconds()).padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
// 启动申请日期实时更新定时器
const startApplyTimeTimer = () => {
// 先清除已存在的定时器
stopApplyTimeTimer()
// 立即更新一次
formData.applyTime = formatDateTime(new Date())
// 每秒更新
applyTimeTimer = setInterval(() => {
formData.applyTime = formatDateTime(new Date())
}, 1000)
}
// 停止申请日期实时更新定时器
const stopApplyTimeTimer = () => {
if (applyTimeTimer) {
clearInterval(applyTimeTimer)
applyTimeTimer = null
}
}
// 新增申请单
async function handleNewApplication() {
const handleNewApplication = async () => {
resetForm()
// 生成新的申请单号
formData.applyNo = await generateApplicationNo()
// 申请单号在保存时由后端生成,此处显示"待生成"
formData.applyNo = '自动生成'
// 申请日期实时更新(启动定时器)
startApplyTimeTimer()
// 确保申请医生是当前登录医生
formData.applyDocName = userNickName.value || userName.value || ''
leftActiveTab.value = 'application'
}
// 查询数据库中是否已存在指定申请单号
const checkApplicationNoExists = async (applyNo) => {
try {
// 这里调用API检查申请单号是否已存在
// 注意你需要根据实际API接口调整此调用
const response = await checkInspectionApplicationNo(applyNo);
// 后端返回格式:如果存在返回{applyNo: "..."}不存在返回null
return response.code === 200 && response.data && response.data.applyNo;
} catch (error) {
return false;
}
};
// 调用后端接口生成申请单号LS + YYYYMMDD + 5位流水号
// 后端使用 Redis 原子递增保证并发安全
const generateApplicationNo = async () => {
try {
const res = await generateInspectionApplyNo();
if (res.code === 200 && res.data && res.data.applyNo) {
return res.data.applyNo;
} else {
return null;
}
} catch (error) {
return null;
}
};
// 重置表单
async function resetForm() {
const resetForm = async () => {
Object.assign(formData, {
applicationId: null,
applyOrganizationId: props.patientInfo.orgId || '',
patientName: props.patientInfo.patientName || '',
medicalrecordNumber: props.patientInfo.identifierNo || '',
natureofCost: 'self',
applyTime: new Date(),
applyTime: '', // 申请日期由定时器实时更新
applyDepartment: props.patientInfo.organizationName || '',
applyDeptCode: props.patientInfo.organizationName,
applyDocCode: userId.value || '',
@@ -1164,9 +1168,10 @@ async function resetForm() {
}
// 保存
function handleSave() {
// 如果正在保存,直接返回
const handleSave = () => {
// P1防重复提交 - 立即设置标志
if (saving.value) return
saving.value = true
// 重置验证错误状态
Object.keys(validationErrors).forEach(key => {
@@ -1175,6 +1180,20 @@ function handleSave() {
let hasErrors = false
// P0检查患者信息是否已加载
if (!formData.patientName?.trim() || !formData.medicalrecordNumber?.trim()) {
ElMessage.error('患者信息未加载,请稍后重试')
saving.value = false
return
}
// P0检查就诊信息是否有效
if (!formData.encounterId) {
ElMessage.error('就诊信息无效,请重新选择患者')
saving.value = false
return
}
// 检查必填字段,执行科室
if (!formData.executeDepartment) {
validationErrors.executeDepartment = true
@@ -1205,16 +1224,14 @@ function handleSave() {
validationErrors.labApplyItemList = true
hasErrors = true
ElMessage.error('请至少选择一项检验项目')
saving.value = false
return
}
// 检查必填字段,申请日期
if(!formData.applyTime || (typeof formData.applyTime === 'string' && !formData.applyTime?.trim())) {
validationErrors.applyTime = true
hasErrors = true
}
// 申请日期由后端在保存时自动生成,无需前端校验
if (hasErrors) {
ElMessage.error('请填写所有必填字段')
saving.value = false
return
}
@@ -1229,87 +1246,36 @@ function handleSave() {
...formData,
labApplyItemList,
physicalExamination: formData.physicalExam, // 字段名映射:前端 physicalExam -> 后端 physicalExamination
inspectionItemsText: selectedInspectionItems.value.map(item => item.itemName).join('+'),
amount: selectedInspectionItems.value.reduce((sum, item) => sum + item.itemAmount, 0),
serviceFee: selectedInspectionItems.value.reduce((sum, item) => sum + (item.serviceFee || 0), 0),
totalAmount: selectedInspectionItems.value.reduce((sum, item) => sum + item.itemAmount + (item.serviceFee || 0), 0)
inspectionItemsText: selectedInspectionItems.value.map(item => item.itemName).join('+')
// 金额由后端计算,前端不传递
}
}
// 先检查申请单号是否已存在
if (isGeneratingNewApplyNo.value) {
// 正在使用新生成的单号,直接保存
isGeneratingNewApplyNo.value = false; // 重置标志
const saveData = prepareSaveData();
executeSave(saveData);
return;
}
checkInspectionApplicationNo(formData.applyNo).then((res) => {
if (res.code === 200 && res.data) {
// res.data有值表示申请单存在
// 注意res.data可能是{applyNo: "..."}或包含其他字段
const exists = res.data && (res.data.applyNo || res.data.exists);
if (exists) {
// 申请单号已存在,提示用户
ElMessageBox.confirm(
'当前申请单号已重复,将重新为您生成新的申请单号',
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
).then(() => {
// 设置标志,表示正在生成新申请单号
isGeneratingNewApplyNo.value = true;
// 重新生成申请单号
generateApplicationNo().then((newApplyNo) => {
if (newApplyNo && newApplyNo.trim() !== '') {
formData.applyNo = newApplyNo;
ElMessage.success('新申请单号已生成,请点击保存按钮继续,请核对数据');
// 不自动保存,等待用户再次点击保存
} else {
isGeneratingNewApplyNo.value = false;
}
});
}).catch((error) => {
// 用户点击取消或其他原因导致的拒绝
if (error !== 'cancel' && error !== 'close') {
}
});
} else {
// 申请单号不存在,继续保存操作
const saveData = prepareSaveData();
executeSave(saveData);
}
} else {
// 其他情况继续保存
const saveData = prepareSaveData();
executeSave(saveData);
}
}).catch(() => {
// 如果检查过程出错,仍然继续保存操作,以免影响正常使用
const saveData = prepareSaveData();
executeSave(saveData);
})
// 申请单号由后端在保存时生成,直接保存
const saveData = prepareSaveData();
executeSave(saveData);
}
const executeSave = (saveData) => {
saving.value = true
saveInspectionApplication(saveData).then((res) => {
if (res.code === 200) {
ElMessage.success('保存成功')
// 停止申请日期实时更新
stopApplyTimeTimer()
// 从后端返回获取生成的申请单号
if (res.data && res.data.applyNo) {
ElMessage.info(`申请单号:${res.data.applyNo}`)
// 显示后端返回的实际申请时间
if (res.data.applyTime) {
formData.applyTime = res.data.applyTime
}
}
emit('save', res.data) // 通知父组件保存成功
resetForm()
// 生成新的申请单号
generateApplicationNo().then((newApplyNo) => {
if (newApplyNo && newApplyNo.trim() !== '') {
formData.applyNo = newApplyNo;
} else {
}
});
// 设置下一个新单为"待生成"
formData.applyNo = '自动生成'
// 启动新的申请日期实时更新
startApplyTimeTimer()
leftActiveTab.value = 'application'
// 刷新列表
getInspectionList()
@@ -1331,7 +1297,9 @@ const executeSave = (saveData) => {
// 查看详情
function handleView(row) {
const handleView = (row) => {
// 停止申请日期实时更新(查看已保存的申请单)
stopApplyTimeTimer()
// 加载表单数据
Object.assign(formData, row)
@@ -1350,7 +1318,7 @@ function handleView(row) {
}
// 切换分类(修改为懒加载)
function switchCategory(category) {
const switchCategory = (category) => {
if (activeCategory.value === category) {
// 如果点击的是当前激活的分类,则收起
activeCategory.value = ''
@@ -1365,7 +1333,7 @@ function switchCategory(category) {
inspectionCategories.value.forEach(cat => {
cat.expanded = cat.key === category
})
// 懒加载该分类的项目
const targetCategory = inspectionCategories.value.find(c => c.key === category)
if (targetCategory && !targetCategory.loaded) {
@@ -1375,17 +1343,17 @@ function switchCategory(category) {
}
// 处理项目项点击(排除勾选框点击)
function handleItemClick(item) {
const handleItemClick = (item) => {
toggleInspectionItem(item)
}
// 判断项目是否已选择
function isItemSelected(item) {
const isItemSelected = (item) => {
return selectedInspectionItems.value.some(selected => selected.itemId === item.itemId)
}
// 切换检验项目选择
function toggleInspectionItem(item) {
const toggleInspectionItem = (item) => {
const index = selectedInspectionItems.value.findIndex(selected => selected.itemId === item.itemId)
if (index > -1) {
selectedInspectionItems.value.splice(index, 1)
@@ -1400,7 +1368,7 @@ function toggleInspectionItem(item) {
}
// 移除检验项目
function removeInspectionItem(item) {
const removeInspectionItem = (item) => {
const index = selectedInspectionItems.value.findIndex(selected => selected.itemId === item.itemId)
if (index > -1) {
selectedInspectionItems.value.splice(index, 1)
@@ -1408,27 +1376,27 @@ function removeInspectionItem(item) {
}
// 清空所有选择
function clearAllSelected() {
const clearAllSelected = () => {
selectedInspectionItems.value = []
}
// 分页大小改变
function handleSizeChange(size) {
const handleSizeChange = (size) => {
queryParams.pageSize = size
getInspectionList()
}
function handleCurrentChange(page) {
const handleCurrentChange = (page) => {
queryParams.pageNo = page
getInspectionList()
}
// 选择框变化
function handleSelectionChange(selection) {
const handleSelectionChange = (selection) => {
selectedRows.value = selection
}
function handlePrint(row) {
const handlePrint = (row) => {
// 切换到申请单TAB
leftActiveTab.value = 'application'
@@ -1449,7 +1417,7 @@ function handlePrint(row) {
}
// 删除申请单
function handleDelete(row) {
const handleDelete = (row) => {
ElMessageBox.confirm(
`确定要删除申请单 "${row.applyNo}" 吗?此操作将同时删除对应的医嘱。`,
'删除确认',
@@ -1480,7 +1448,7 @@ function handleDelete(row) {
}
// 单元格点击 - 点击表格行时加载申请单详情
function handleCellClick(row, column) {
const handleCellClick = (row, column) => {
// 点击表格行时,将该申请单的数据加载到表单中
// 使用 applyNo 判断是否有效
if (row && row.applyNo) {
@@ -1489,7 +1457,7 @@ function handleCellClick(row, column) {
}
// 行点击事件处理
function handleRowClick(currentRow, oldRow) {
const handleRowClick = (currentRow, oldRow) => {
// 点击表格行时,将该申请单的数据加载到表单中
// 使用 applyNo 判断是否有效
if (currentRow && currentRow.applyNo) {
@@ -1498,7 +1466,9 @@ function handleRowClick(currentRow, oldRow) {
}
// 提取公共方法加载申请单到表单
async function loadApplicationToForm(row) {
const loadApplicationToForm = async (row) => {
// 停止申请日期实时更新(加载已保存的申请单)
stopApplyTimeTimer()
// 切换到申请单 TAB
leftActiveTab.value = 'application'
@@ -1640,6 +1610,11 @@ onMounted(async () => {
await loadInspectionData()
})
// 组件卸载时清除定时器
onUnmounted(() => {
stopApplyTimeTimer()
})
// 暴露方法
defineExpose({
getList: getInspectionList

View File

@@ -191,12 +191,18 @@
<template v-if="scope.row.adviceType == 1">
<div style="display: flex; align-items: center; margin-bottom: 16px; gap: 16px">
<span class="medicine-title">
{{ scope.row.adviceName }} {{ scope.row.volume }}
<template v-if="scope.row.partPercent !== null && scope.row.partPercent !== undefined && scope.row.partPercent - 1 > 0">
[1{{ scope.row.unitCode_dictText }}={{ scope.row.partPercent }}{{ scope.row.minUnitCode_dictText }}]
</template>
[{{ (scope.row.unitPrice !== undefined && scope.row.unitPrice !== null && !isNaN(scope.row.unitPrice) &&
isFinite(scope.row.unitPrice)) ? Number(scope.row.unitPrice).toFixed(2) : '-' }} /{{ scope.row.unitCode_dictText }}]
{{
scope.row.adviceName +
' ' +
scope.row.volume +
' [' +
(scope.row.unitPrice !== undefined && scope.row.unitPrice !== null && !isNaN(scope.row.unitPrice) &&
isFinite(scope.row.unitPrice) ? Number(scope.row.unitPrice).toFixed(2) : '-') +
' 元' +
'/' +
scope.row.unitCode_dictText +
']'
}}
</span>
<el-form-item prop="lotNumber" label="药房:">
<el-select v-model="scope.row.inventoryId" style="width: 330px; margin-right: 20px"
@@ -261,7 +267,6 @@
<div class="form-group">
<el-form-item label="给药途径:" prop="methodCode" class="required-field" data-prop="methodCode">
<el-select v-model="scope.row.methodCode" placeholder="给药途径" clearable filterable
style="width: 150px"
:ref="(el) => (inputRefs.methodCode = el)" @keyup.enter.prevent="
() => {
if (scope.row.methodCode) {
@@ -284,9 +289,7 @@
}
// inputRefs.rateCode.blur();
}
"
@change="calculateTotalAmount(scope.row, scope.$index)"
:ref="(el) => (inputRefs.rateCode = el)">
" :ref="(el) => (inputRefs.rateCode = el)">
<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>
@@ -307,10 +310,7 @@
controls-position="right" :controls="false" :ref="(el) => (inputRefs.dispensePerDuration = el)"
@keyup.enter.prevent="
handleEnter('dispensePerDuration', scope.row, scope.$index)
"
@change="calculateTotalAmount(scope.row, scope.$index)"
@blur="calculateTotalAmount(scope.row, scope.$index)"
@input="calculateTotalAmount(scope.row, scope.$index)">
">
<template #suffix></template>
</el-input-number>
</el-form-item>
@@ -321,8 +321,8 @@
@keyup.enter.prevent="handleEnter('quantity', scope.row, scope.$index)"
@input="calculateTotalPrice(scope.row, scope.$index)" />
</el-form-item>
<el-form-item style="margin-left: -10px;">
<el-select v-model="scope.row.unitCode" style="width: 70px; margin-right: 10px" placeholder=" "
<el-form-item>
<el-select v-model="scope.row.unitCode" style="width: 70px; margin-right: 20px" placeholder=" "
@change="calculateTotalAmount(scope.row, scope.$index)">
<template v-for="item in scope.row.unitCodeList" :key="item.value">
<el-option v-if="checkUnit(item, scope.row)" :value="item.value" :label="item.label" @click="
@@ -342,13 +342,7 @@
" />
</template>
</el-select>
<span v-if="scope.row.unitCode_dictText" class="unit-text">{{ scope.row.unitCode_dictText }}</span>
</el-form-item>
<!-- 🔧 Bug #273 拆零比提示 -->
<span v-if="scope.row.partPercent !== null && scope.row.partPercent !== undefined && scope.row.partPercent - 1 > 0 && scope.row.unitCode !== scope.row.minUnitCode"
class="part-percent-hint">
{{ scope.row.partPercent }}<template v-if="scope.row.minUnitCode_dictText">{{ scope.row.minUnitCode_dictText }}</template><template v-else>袋</template>/<template v-if="scope.row.unitCode_dictText">{{ scope.row.unitCode_dictText }}</template><template v-else>盒</template>
</span>
</div>
</div>
<div style="
@@ -687,6 +681,7 @@
style="width: 60px"
size="small"
@change="calculateTotalPrice(scope.row, scope.$index)"
@input="calculateTotalPrice(scope.row, scope.$index)"
/>
<span style="margin-left: 4px">{{ scope.row.unitCode_dictText }}</span>
</template>
@@ -734,7 +729,7 @@
size="small"
/>
<span style="margin: 0 2px; font-size: 12px;">天</span>
<el-select v-model="scope.row.methodCode" size="small" style="width: 120px" placeholder="用法">
<el-select v-model="scope.row.methodCode" size="small" style="width: 65px" placeholder="用法">
<el-option v-for="item in method_code" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</template>
@@ -1580,11 +1575,6 @@ function getListInfo(addNewRow) {
console.log('BugFix#219: 过滤掉已作废的会诊医嘱, requestId=', item.requestId);
return false;
}
// 🔧 Bug Fix: 过滤掉项目名称为空的无效医嘱
if (!item.adviceName || item.adviceName.trim() === '') {
console.log('BugFix: 过滤掉空白医嘱, requestId=', item.requestId, 'adviceType=', item.adviceType);
return false;
}
return true;
});
@@ -2028,7 +2018,6 @@ function handleDelete() {
adviceType: item.adviceType,
statusEnum: item.statusEnum,
requestId: item.requestId,
adviceName: item.adviceName
})));
// 🔧 BugFix: 放宽条件只要有requestId的会诊医嘱都可以尝试删除
@@ -2133,18 +2122,13 @@ function handleDelete() {
adviceType: item.adviceType,
statusEnum: item.statusEnum,
requestId: item.requestId,
adviceName: item.adviceName,
uniqueKey: item.uniqueKey
})));
console.log('BugFix#219: prescriptionList 中的所有医嘱=', prescriptionList.value.map(item => ({
adviceType: item.adviceType,
statusEnum: item.statusEnum,
adviceName: item.adviceName,
uniqueKey: item.uniqueKey,
totalPrice: item.totalPrice,
dose: item.dose,
doseQuantity: item.doseQuantity
uniqueKey: item.uniqueKey
})));
for (let i = prescriptionList.value.length - 1; i >= 0; i--) {
@@ -2333,10 +2317,14 @@ function handleSave(prescriptionId) {
// 签发核心逻辑
function executeSaveLogic() {
// 🔧 Bug Fix: 获取当前选中的费用性质,保持字符串类型避免大整数精度丢失
// 🔧 Bug Fix: 获取当前选中的费用性质,如果是'ZIFEI'或0则转为null让后端查询默认账户
let finalAccountId = accountId.value;
if (finalAccountId === 'ZIFEI' || finalAccountId === 0) {
finalAccountId = null;
} else if (finalAccountId && !isNaN(Number(finalAccountId))) {
finalAccountId = Number(finalAccountId);
} else {
finalAccountId = null;
}
// 🔧 Bug Fix: 校验患者信息完整性
@@ -2349,7 +2337,11 @@ function handleSave(prescriptionId) {
saveList.forEach((item) => {
item.patientId = props.patientInfo.patientId;
item.encounterId = props.patientInfo.encounterId;
item.accountId = finalAccountId;
// 🔧 使用项目自己的 accountId,而不是全局的 finalAccountId
// 这样自动带出的耗材才能保持与药品相同的账户
if (!item.accountId) {
item.accountId = finalAccountId;
}
item.dbOpType = '1';
});
@@ -2359,7 +2351,7 @@ function handleSave(prescriptionId) {
// 先解析contentJson
let parsedContent = JSON.parse(item.contentJson || '{}');
// 🔧 Bug Fix: 强制将accountId设为正确的值
parsedContent.accountId = finalAccountId;
parsedContent.accountId = item.accountId || finalAccountId; // 🔧 使用 item 自己的 accountId
// --- 【修改点2Bug 2 修复 (单位换算)】 ---
let finalQuantity = item.quantity;
@@ -2426,7 +2418,6 @@ function handleSave(prescriptionId) {
}
console.log('保存医嘱参数:', {
adviceName: item.adviceName,
adviceType: item.adviceType,
saveAdviceType: saveAdviceType,
adviceTableName: adviceTableNameVal,
@@ -2452,15 +2443,28 @@ function handleSave(prescriptionId) {
adviceTableName: adviceTableNameVal,
locationId: locationIdVal,
// 🔧 确保 methodCode 被传递(用于触发耗材绑定逻辑)
methodCode: item.methodCode || parsedContent.methodCode
methodCode: item.methodCode || parsedContent.methodCode,
// 🔧 确保 accountId 被传递(用于预结算)
accountId: item.accountId || parsedContent.accountId,
// 🔧 更新 contentJson 中的 adviceType确保后端分类正确
contentJson: JSON.stringify({
...parsedContent,
adviceType: saveAdviceType
})
};
});
// 提交签发请求
isSaving.value = true;
console.log('签发处方参数:', {
organizationId: props.patientInfo.orgId,
adviceSaveList: list,
console.log('organizationId:', props.patientInfo.orgId);
console.log('adviceSaveList:', list);
list.forEach((item, idx) => {
console.log(`项目${idx + 1}:`, {
adviceName: item.adviceName,
adviceType: item.adviceType,
accountId: item.accountId,
methodCode: item.methodCode
});
});
savePrescriptionSign({
@@ -2613,7 +2617,8 @@ function handleOrderBindInfo(bindIdInfo, currentMethodCode) {
uniqueKey: nextId.value++,
patientId: props.patientInfo.patientId,
encounterId: props.patientInfo.encounterId,
accountId: accountId.value,
accountId: accountId.value, // 🔧 使用当前全局的 accountId
requestId: undefined, // 🔧 耗材的 requestId 设为 undefined让后端自动生成
quantity: item.quantity,
methodCode: item.methodCode, // 🔧 现在 item.methodCode 有值了
rateCode: item.rateCode,
@@ -2624,7 +2629,7 @@ function handleOrderBindInfo(bindIdInfo, currentMethodCode) {
unitCode: item.unitCode,
unitCode_dictText: item.unitCodeName || '',
statusEnum: 1,
dbOpType: prescriptionList.value[rowIndex.value].requestId ? '2' : '1',
dbOpType: '1', // 🔧 新耗材总是 INSERT
conditionId: conditionId.value,
conditionDefinitionId: conditionDefinitionId.value,
encounterDiagnosisId: encounterDiagnosisId.value,
@@ -2902,10 +2907,11 @@ function handleSaveBatch(prescriptionId) {
}
// 🔧 Bug Fix: 在保存时才转换 accountId
// 保持为字符串类型,避免 JavaScript 大整数精度丢失问题
let finalAccountId = accountId.value;
if (finalAccountId === 'ZIFEI' || finalAccountId === 0) {
if (finalAccountId === 'ZIFEI') {
finalAccountId = null;
} else if (finalAccountId && !isNaN(Number(finalAccountId))) {
finalAccountId = Number(finalAccountId);
}
// 更新到处方对象
@@ -3006,10 +3012,12 @@ function handleSaveBatch(prescriptionId) {
};
const contentJson = JSON.stringify(itemToSave);
// 🔧 Bug Fix: 处理accountId保持字符串类型避免大整数精度丢失
// 🔧 Bug Fix: 处理accountId如果是'ZIFEI'或0则转为null让后端查询默认账户
let itemAccountId = finalAccountId;
if (itemAccountId === 'ZIFEI' || itemAccountId === 0) {
itemAccountId = null;
} else if (itemAccountId && !isNaN(Number(itemAccountId))) {
itemAccountId = Number(itemAccountId);
}
// 🔧 Bug Fix: 确保库存匹配成功的关键字段
@@ -3183,12 +3191,6 @@ function syncGroupFields(row) {
}
function setValue(row) {
// 🔧 Bug Fix: 强制设置耗材类型,确保 adviceType 为 4
// 如果 adviceTableName 是 adm_device_definition强制设为耗材类型
if (row.adviceTableName === 'adm_device_definition') {
row.adviceType = 4;
}
unitCodeList.value = [];
unitCodeList.value.push({ value: row.unitCode, label: row.unitCode_dictText, type: 'unit' });
unitCodeList.value.push({
@@ -3561,7 +3563,6 @@ function handleSingOut() {
let selectRows = prescriptionRef.value.getSelectionRows();
console.log('BugFix#219: handleSingOut called, selectRows=', selectRows);
console.log('BugFix#219: 选中行详情:', selectRows.map(item => ({
adviceName: item.adviceName,
adviceType: item.adviceType,
statusEnum: item.statusEnum,
statusEnumType: typeof item.statusEnum,
@@ -3579,7 +3580,6 @@ function handleSingOut() {
console.log('BugFix#219: consultationRows=', consultationRows.length, 'normalRows=', normalRows.length);
console.log('BugFix#219: normalRows详情:', normalRows.map(item => ({
adviceName: item.adviceName,
statusEnum: item.statusEnum,
statusEnumType: typeof item.statusEnum,
requestId: item.requestId
@@ -3592,7 +3592,6 @@ function handleSingOut() {
adviceType: item.adviceType,
statusEnum: item.statusEnum,
requestId: item.requestId,
adviceName: item.adviceName
})));
// 🔧 BugFix: 放宽条件只要有requestId的会诊医嘱都可以处理
@@ -3695,7 +3694,6 @@ function handleSingOut() {
adviceType: item.adviceType,
statusEnum: item.statusEnum,
requestId: item.requestId,
adviceName: item.adviceName
})));
// 🔧 BugFix: 将requestId转换为数字类型
@@ -3935,9 +3933,8 @@ function convertValues(row, index) {
row.dose = row.doseQuantity / row.partPercent;
break;
}
// 🔧 Bug #273 修复:单次剂量变化后重新计算总量
calculateTotalAmount(row, index);
});
// calculateTotalAmount(row, index);
}
// 单次剂量数量改变时自动计算总量
@@ -3963,106 +3960,25 @@ function convertDoseValues(row, index) {
row.doseQuantity = row.dose * row.partPercent;
break;
}
// 🔧 Bug #273 修复:单次剂量变化后重新计算总量
calculateTotalAmount(row, index);
});
// calculateTotalAmount(row, index);
}
// 总量计算,仅适用只有两种单位的情况
function calculateTotalAmount(row, index) {
nextTick(() => {
// 🔧 Bug #273 调试日志
console.log('[calculateTotalAmount] 开始计算', {
adviceType: row.adviceType,
rateCode: row.rateCode,
dispensePerDuration: row.dispensePerDuration,
doseQuantity: row.doseQuantity,
partAttributeEnum: row.partAttributeEnum,
unitCode: row.unitCode,
minUnitCode: row.minUnitCode
});
// 项目为西药或中成药时
if (row.adviceType != 1 && row.adviceType != 2) {
console.log('[calculateTotalAmount] 非西药/中成药,跳过计算');
if (row.adviceType == 2) {
calculateTotalPrice(row, index);
return;
}
// 🔧 Bug #273 修复:使用字典数据计算频次对应的每日次数
function getRateCount(rateCode) {
// 先从字典中查找
const rateDict = rate_code.value?.find(item => item.value === rateCode);
if (rateDict && rateDict.remark) {
return Number(rateDict.remark);
}
// 回退到固定映射
const frequencyMap = {
ST: 1, QD: 1, BID: 2, TID: 3, QID: 4, QN: 1,
QOD: 0.5, QW: 1/7, BIW: 2/7, TIW: 3/7, QOW: 1/14
};
return frequencyMap[rateCode] || 1;
}
// 情况1: 根据用药天数和频次计算
if (row.rateCode && row.dispensePerDuration) {
const rateCount = getRateCount(row.rateCode);
const count = rateCount * row.dispensePerDuration;
console.log('[calculateTotalAmount] 计算count:', { rateCount, days: row.dispensePerDuration, count });
if (!count) return;
let quantity;
if (row.unitCode == row.minUnitCode) {
// 🔧 使用最小单位时,计算的是最小单位数量(如袋数)
quantity = calculateQuantityBySplitType(row.partAttributeEnum, row.doseQuantity, count, row.partPercent);
row.quantity = quantity;
row.totalPrice = (quantity * row.minUnitPrice).toFixed(2);
console.log('[calculateTotalAmount] 计算结果(最小单位):', { quantity, totalPrice: row.totalPrice, partPercent: row.partPercent });
} else {
// 🔧 使用包装单位时,计算的是包装单位数量(如盒数)
quantity = calculateQuantity(
row.partAttributeEnum,
row.doseQuantity,
count,
row.partPercent
);
row.quantity = quantity;
row.totalPrice = (quantity * row.unitPrice).toFixed(2);
console.log('[calculateTotalAmount] 计算结果(包装单位):', { quantity, totalPrice: row.totalPrice, partPercent: row.partPercent });
}
return;
}
console.log('[calculateTotalAmount] 条件不满足,未计算(rateCode或dispensePerDuration为空)');
});
}
// 总量计算,仅适用只有两种单位的情况
function calculateTotalAmount2(row, index) {
nextTick(() => {
// 项目为西药或中成药时
if (row.adviceType != 1 && row.adviceType != 2) {
return;
}
// 🔧 Bug #273 修复:使用字典数据计算频次对应的每日次数
function getRateCount(rateCode) {
// 先从字典中查找
const rateDict = rate_code.value?.find(item => item.value === rateCode);
if (rateDict && rateDict.remark) {
return Number(rateDict.remark);
}
// 回退到固定映射
const frequencyMap = {
ST: 1, QD: 1, BID: 2, TID: 3, QID: 4, QN: 1,
QOD: 0.5, QW: 1/7, BIW: 2/7, TIW: 3/7, QOW: 1/14
};
return frequencyMap[rateCode] || 1;
}
// 情况1: 根据用药天数和频次计算
if (row.rateCode && row.dispensePerDuration) {
const rateCount = getRateCount(row.rateCode);
const count = rateCount * row.dispensePerDuration;
const count = calculateQuantityByDays(row.rateCode, row.dispensePerDuration);
if (!count) return;
let quantity;
@@ -4082,62 +3998,100 @@ function calculateTotalAmount2(row, index) {
}
return;
}
// 情况2: 中成药兼容旧逻辑
if (row.adviceType == 2) {
if (row.partPercent == 1) {
row.totalPrice = (row.quantity * row.unitPrice).toFixed(6);
} else {
// 拆零比不为1时 如果当前总量单位是大单位,总价等于数量乘以大单位价格 否则总价等于数量乘以小单位价格
if (row.unitCodeList.find((k) => k.value == row.unitCode).type == 'unit') {
row.totalPrice = (row.quantity * row.unitPrice).toFixed(6);
} else {
row.totalPrice = (row.quantity * row.minUnitPrice).toFixed(6);
}
}
} else if (row.adviceType == 1) {
if (row.rateCode && row.dispensePerDuration) {
// 根据用药天数和用药频次计算数量,医生按顺序填的情况
let count = calculateQuantityByDays(row.rateCode, row.dispensePerDuration);
if (count) {
let quantity;
if (row.unitCode == row.minUnitCode) {
quantity = calculateQuantityBySplitType(row.partAttributeEnum, row.doseQuantity, count);
prescriptionList.value[index].quantity = quantity;
prescriptionList.value[index].totalPrice = (quantity * row.minUnitPrice).toFixed(6);
} else {
quantity = calculateQuantity(
row.partAttributeEnum,
row.doseQuantity,
count,
row.partPercent
);
prescriptionList.value[index].quantity = quantity;
prescriptionList.value[index].totalPrice = (quantity * row.unitPrice).toFixed(6);
}
}
} else if (row.quantity) {
// 如果医生开药先填总量 直接计算总价格
if (row.unitCode == row.minUnitCode) {
prescriptionList.value[index].totalPrice = (row.quantity * row.minUnitPrice).toFixed(6);
} else {
prescriptionList.value[index].totalPrice = (row.quantity * row.unitPrice).toFixed(6);
}
}
return;
}
// 情况3: 医生先填总量
if (row.quantity) {
if (row.unitCode == row.minUnitCode) {
prescriptionList.value[index].totalPrice = (row.quantity * row.minUnitPrice).toFixed(6);
} else {
prescriptionList.value[index].totalPrice = (row.quantity * row.unitPrice).toFixed(6);
}
}
});
}
/**
* 根据门诊拆分类型计算总药量 - 最小单位
* 🔧 Bug #273 修复:统一使用拆零比计算
* 根据门诊拆分类型计算总药量
*
* @param type 门诊拆分类型
* @param dose 单次剂量 最小单位
* @param count 用药频次和用药天数计算出的总数
* @param partPercent 拆零比
*/
function calculateQuantityBySplitType(type, dose, count, partPercent) {
// 先计算最小单位总量
const minUnitTotal = dose * count;
function calculateQuantityBySplitType(type, dose, count) {
switch (type) {
case 1: // 门诊按最小单位每次量向上取整
// 每次用量向上取整,然后计算总量
return Math.ceil(Math.ceil(dose) * count / partPercent);
return Math.ceil(dose) * count;
case 2: // 门诊按包装单位不可拆分
// 总量向上取整到包装单位
return Math.ceil(minUnitTotal / partPercent);
return Math.ceil(dose * count);
case 3: // 门诊按最小单位总量向上取整
// 总量向上取整,然后转换为包装单位
return Math.ceil(minUnitTotal / partPercent);
return Math.ceil(dose * count);
case 4: // 门诊按包装单位每次量向上取整
// 每次用量转换为包装单位后向上取整
return Math.ceil(Math.ceil(dose / partPercent) * count);
return Math.ceil(dose) * count;
}
}
/**
* 根据门诊拆分类型计算总药量 - 包装单位
* 🔧 Bug #273 修复:统一使用拆零比计算
* 根据门诊拆分类型计算总药量
*
* @param type 门诊拆分类型
* @param dose 单次剂量 最小单位
* @param count 用药频次和用药天数计算出的总数
* @param partPercent 拆零比
*/
function calculateQuantity(type, dose, count, partPercent) {
// 先计算最小单位总量
const minUnitTotal = dose * count;
switch (type) {
case 1: // 门诊按最小单位每次量向上取整
// 每次用量向上取整,然后计算总量
return Math.ceil(Math.ceil(dose) * count / partPercent);
return Math.ceil(dose / partPercent) * count;
case 2: // 门诊按包装单位不可拆分
// 总量向上取整到包装单位
return Math.ceil(minUnitTotal / partPercent);
return Math.ceil(dose * count);
case 3: // 门诊按最小单位总量向上取整
// 总量向上取整,然后转换为包装单位
return Math.ceil(minUnitTotal / partPercent);
return Math.ceil((dose / partPercent) * count);
case 4: // 门诊按包装单位每次量向上取整
// 每次用量转换为包装单位后向上取整
return Math.ceil(Math.ceil(dose / partPercent) * count);
return Math.ceil(dose) * count;
}
}
@@ -4521,16 +4475,6 @@ defineExpose({ getListInfo, getDiagnosisInfo });
margin-bottom: 0px;
}
/* 🔧 Bug #273 拆零比提示样式 */
.part-percent-hint {
color: #909399;
font-size: 12px;
white-space: nowrap;
margin-left: 8px;
display: inline-flex;
align-items: center;
}
/* V1.3 风格侧边栏 - 组套列表样式 */
.order-group-container {
display: flex;

View File

@@ -220,7 +220,7 @@
"
>
<el-option
v-for="item in doctorInfoOptions"
v-for="item in residentDoctorOptions"
:key="item.id"
:label="item.name"
:value="item.id"
@@ -239,7 +239,7 @@
"
>
<el-option
v-for="item in doctorInfoOptions"
v-for="item in attendingDoctorOptions"
:key="item.id"
:label="item.name"
:value="item.id"
@@ -258,7 +258,7 @@
"
>
<el-option
v-for="item in doctorInfoOptions"
v-for="item in chiefDoctorOptions"
:key="item.id"
:label="item.name"
:value="item.id"
@@ -332,7 +332,7 @@
</el-dialog>
</template>
<script lang="ts" setup>
import {nextTick, onMounted, reactive, ref, watch} from 'vue';
import {computed, nextTick, onMounted, reactive, ref, watch} from 'vue';
import type {FormInstance, FormRules} from 'element-plus';
import {dayjs, ElMessage} from 'element-plus';
// import type { IInPatient } from '@/model/IInPatient'
@@ -353,8 +353,23 @@ const props = defineProps({
const currentInPatient = ref<Partial<IInPatient>>({});
const bedInfoOptions = ref<{ label: string; value: string }[]>([]);
const doctorInfoOptions = ref<{ name: string; id: string }[]>([]);
const doctorInfoOptions = ref<{ name: string; id: string; drProfttlCode?: string }[]>([]);
const nurseInfoOptions = ref<{ name: string; practitionerId: string }[]>([]);
// 住院医生只显示医师职称编码234
const residentDoctorOptions = computed(() => {
return doctorInfoOptions.value.filter(item => item.drProfttlCode === '234');
});
// 主治医生只显示主治医师职称编码233
const attendingDoctorOptions = computed(() => {
return doctorInfoOptions.value.filter(item => item.drProfttlCode === '233');
});
// 主任医生显示副主任医师232和主任医师231
const chiefDoctorOptions = computed(() => {
return doctorInfoOptions.value.filter(item => item.drProfttlCode === '231' || item.drProfttlCode === '232');
});
const InitInfoOptions = ref<any>({});
const priorityListOptions = ref<{ info: string; value: string }[]>([]);
const pendingInfo = ref<any>({});
@@ -407,13 +422,28 @@ const loadPatientInfo = () => {
console.log('chiefDoctorId:', res.data.chiefDoctorId);
console.log('primaryNurseId:', res.data.primaryNurseId);
if (res.data.admittingDoctorId) {
interventionForm.value.admittingDoctorId = String(res.data.admittingDoctorId);
const doctorId = String(res.data.admittingDoctorId);
// 检查该医生是否在住院医生列表中
const existsInResident = residentDoctorOptions.value.some(item => item.id === doctorId);
if (existsInResident) {
interventionForm.value.admittingDoctorId = doctorId;
}
}
if (res.data.attendingDoctorId) {
interventionForm.value.attendingDoctorId = String(res.data.attendingDoctorId);
const doctorId = String(res.data.attendingDoctorId);
// 检查该医生是否在主治医生列表中
const existsInAttending = attendingDoctorOptions.value.some(item => item.id === doctorId);
if (existsInAttending) {
interventionForm.value.attendingDoctorId = doctorId;
}
}
if (res.data.chiefDoctorId) {
interventionForm.value.chiefDoctorId = String(res.data.chiefDoctorId);
const doctorId = String(res.data.chiefDoctorId);
// 检查该医生是否在主任医生列表中
const existsInChief = chiefDoctorOptions.value.some(item => item.id === doctorId);
if (existsInChief) {
interventionForm.value.chiefDoctorId = doctorId;
}
}
if (res.data.primaryNurseId) {
// 护士ID也转换为字符串以匹配护士选项
@@ -489,18 +519,8 @@ const init = () => {
// 并且只在当前没有选择主任医生时才设置默认值(避免覆盖已从后端获取的数据)
if (props.pendingInfo.entranceType != 1) {
nextTick(() => {
if (doctorInfoOptions.value.length > 0 && !interventionForm.value.chiefDoctorId) {
let selectId = '';
doctorInfoOptions.value.forEach((item: any) => {
if (item.drProfttlCode == '231') {
selectId = item.id;
}
});
if (selectId.length > 0) {
interventionForm.value.chiefDoctorId = selectId;
} else {
interventionForm.value.chiefDoctorId = doctorInfoOptions.value[0].id;
}
if (chiefDoctorOptions.value.length > 0 && !interventionForm.value.chiefDoctorId) {
interventionForm.value.chiefDoctorId = chiefDoctorOptions.value[0].id;
}
});
}

View File

@@ -546,6 +546,7 @@ function loadPackageData(data) {
frequency: item.frequency || '',
days: item.days || '',
quantity: item.quantity || 1,
originalUnitPrice: item.originalUnitPrice || item.unitPrice || 0,
unitPrice: item.unitPrice || 0,
unit: item.unit || '',
amount: item.amount || 0,
@@ -908,6 +909,7 @@ function handleAddRow() {
days: '',
quantity: 1,
unit: '',
originalUnitPrice: 0,
unitPrice: 0,
amount: 0,
serviceCharge: 0,
@@ -1058,7 +1060,7 @@ function handleItemSelect(row) {
if (item) {
row.itemName = item.name || item.itemName || ''
row.code = item.busNo || item.code || item.itemCode || ''
row.unitPrice = parseFloat(item.retailPrice || item.unitPrice || item.price || 0)
row.originalUnitPrice = parseFloat(item.retailPrice || item.unitPrice || item.price || 0)
// permittedUnitCode_dictText是字典翻译后的值permittedUnitCode是后端返回的原始值
row.unit = item.permittedUnitCode_dictText || item.permittedUnitCode || ''
@@ -1074,7 +1076,15 @@ function handleItemSelect(row) {
// 计算金额
function calculateAmount(row) {
row.amount = (row.quantity || 0) * (row.unitPrice || 0)
// 获取折扣比例默认100%
const discountRate = formData.discount ? parseFloat(formData.discount) / 100 : 1;
// 单价 = 原项目单价 × 折扣比例
row.unitPrice = row.originalUnitPrice * discountRate;
// 金额 = 折扣单价 × 数量
row.amount = (row.quantity || 0) * row.unitPrice;
calculateTotal(row)
}
@@ -1097,20 +1107,11 @@ function calculateTotalServiceFee() {
formData.serviceFee = totalServiceFee
}
// 计算套餐金额(应用折扣
// 计算套餐金额(套餐金额=套餐明细总金额字段的值之和
function calculatePackagePrice() {
// 计算所有明细项目的总金额
// 套餐金额 = 所有明细项目的总金额之和
const total = detailData.value.reduce((sum, item) => sum + (item.total || 0), 0)
// 如果有折扣,应用折扣计算
let finalPrice = total
if (formData.discount && parseFloat(formData.discount) > 0 && parseFloat(formData.discount) <= 100) {
const discountRate = parseFloat(formData.discount) / 100 // 将百分比转换为小数(如 10% -> 0.1
const discountAmount = total * discountRate // 折扣金额
finalPrice = total - discountAmount // 折扣后金额
}
formData.packagePrice = finalPrice
formData.packagePrice = total
}
// 折扣变更处理
@@ -1134,8 +1135,10 @@ function handleDiscountChange(value) {
}
}
// 重新计算套餐金额
calculatePackagePrice()
// 重新计算所有明细行
detailData.value.forEach(row => {
calculateAmount(row)
})
}
// 套餐管理
@@ -1251,6 +1254,7 @@ async function handleSave() {
days: item.days || '',
quantity: parseInt(item.quantity) || 1,
unit: item.unit || '',
originalUnitPrice: parseFloat(item.originalUnitPrice) || 0,
unitPrice: parseFloat(item.unitPrice) || 0,
amount: parseFloat(item.amount) || 0,
serviceCharge: parseFloat(item.serviceCharge) || 0,