bug 587 588 589 591

This commit is contained in:
Ranyunqiao
2026-06-05 17:15:39 +08:00
parent 09e07b1fba
commit 0f4da1e32f
21 changed files with 390 additions and 129 deletions

View File

@@ -76,7 +76,7 @@ public class OutpatientPricingAppServiceImpl implements IOutpatientPricingAppSer
String categoryCode = adviceBaseDto != null ? adviceBaseDto.getCategoryCode() : null;
// 门诊划价:仅返回划价标记为“是”的项目
return iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, searchKey, locationId, null,
organizationId, pageNo, pageSize, Whether.YES.getValue(), adviceTypes, null, categoryCode);
organizationId, pageNo, pageSize, Whether.YES.getValue(), adviceTypes, null, categoryCode, null);
}
}

View File

@@ -32,7 +32,8 @@ 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, String categoryCode);
Integer pricingFlag, List<Integer> adviceTypes, String orderPricing, String categoryCode,
Integer dischargeFlag);
/**
* 查询医嘱绑定信息

View File

@@ -179,7 +179,8 @@ 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, String categoryCode) {
Integer pricingFlag, List<Integer> adviceTypes, String orderPricing, String categoryCode,
Integer dischargeFlag) {
// 生成缓存键处理可能的null值
String safeSearchKey = searchKey != null ? searchKey : "";
@@ -253,7 +254,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, categoryCode,
adviceTypes, searchKey, categoryCode, dischargeFlag,
queryWrapper);
List<AdviceBaseDto> adviceBaseDtoList = adviceBaseInfo.getRecords();
@@ -680,15 +681,16 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
.filter(e -> e.getAdviceType() != null && e.getAdviceType() == ItemType.DEVICE.getValue())
.collect(Collectors.toList());
// 诊疗分类adviceType == 3
// 诊疗分类adviceType == 3 或 6手术或 8文字医嘱
List<AdviceSaveDto> activityList = adviceSaveList.stream()
.filter(e -> e.getAdviceType() != null
&& (e.getAdviceType() == ItemType.ACTIVITY.getValue()
|| e.getAdviceType() == ItemType.SURGERY.getValue())) // 手术(6)也走诊疗流程
|| e.getAdviceType() == ItemType.SURGERY.getValue()
|| e.getAdviceType() == ItemType.TEXT.getValue())) // 手术(6)和文字医嘱(8)也走诊疗流程
.collect(Collectors.toList());
// 🔍 Debug日志日志: 记录分类结果
log.info("BugFix#219: 医嘱分类完成 - 药品:{}, 耗材:{}, 诊疗:{}",
log.info("BugFix#219: 医嘱分类完成 - 药品:{}, 耗材:{}, 诊疗(含文字):{}",
medicineList.size(), deviceList.size(), activityList.size());
// 🔍 Debug日志: 打印所有医嘱的adviceType
for (AdviceSaveDto dto : adviceSaveList) {
@@ -1340,6 +1342,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
null,
null,
null,
null,
null);
if (devicePage == null || devicePage.getRecords().isEmpty()) {
@@ -1708,6 +1711,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
null,
null,
null,
null,
null);
if (devicePage != null && !devicePage.getRecords().isEmpty()) {
AdviceBaseDto deviceBaseInfo = devicePage.getRecords().get(0);
@@ -2037,14 +2041,19 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// 会诊医嘱category_enum设置为31
serviceRequest.setCategoryEnum(31);
log.info("保存会诊医嘱category_enum=31");
} else if (adviceSaveDto.getAdviceType() != null && adviceSaveDto.getAdviceType() == ItemType.TEXT.getValue()) {
// 文字医嘱category_enum设置为8
serviceRequest.setCategoryEnum(ItemType.TEXT.getValue());
log.info("保存文字医嘱category_enum=8");
} else {
// 普通诊疗医嘱
serviceRequest.setCategoryEnum(adviceSaveDto.getCategoryEnum());
}
// 🔧 BugFix#385: 检查类型(adviceType=2)不走定价体系activityId设置为0L占位
// 🔧 BugFix#385: 检查类型(adviceType=2)和文字医嘱(adviceType=8)不走定价体系activityId设置为0L占位
// 与ExamApplyController保持一致数据库有NOT NULL约束
if (adviceSaveDto.getAdviceType() != null && adviceSaveDto.getAdviceType() == 2) {
if (adviceSaveDto.getAdviceType() != null
&& (adviceSaveDto.getAdviceType() == 2 || adviceSaveDto.getAdviceType() == ItemType.TEXT.getValue())) {
serviceRequest.setActivityId(0L);
} else {
serviceRequest.setActivityId(adviceSaveDto.getAdviceDefinitionId());// 诊疗定义id
@@ -2069,10 +2078,16 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// 备注
serviceRequest.setRemark(adviceSaveDto.getRemark());
// 🔧 文字医嘱:设置频次和开始时间
if (adviceSaveDto.getAdviceType() != null && adviceSaveDto.getAdviceType() == ItemType.TEXT.getValue()) {
serviceRequest.setRateCode(adviceSaveDto.getRateCode());
serviceRequest.setOccurrenceStartTime(adviceSaveDto.getStartTime());
}
iServiceRequestService.saveOrUpdate(serviceRequest);
// 保存时保存诊疗费用项
if (is_save) {
// 保存时保存诊疗费用项(文字医嘱跳过计费)
if (is_save && !(adviceSaveDto.getAdviceType() != null && adviceSaveDto.getAdviceType() == ItemType.TEXT.getValue())) {
chargeItem = new ChargeItem();
chargeItem.setId(adviceSaveDto.getChargeItemId()); // 费用项id
chargeItem.setTenantId(tenantId); // 补全租户ID
@@ -2169,9 +2184,12 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// }
// log.error(e.getMessage(), e);
// }
// 🔧 BugFix#328: 签发时将收费项目状态从草稿改为待收费
// 修复检验申请单生成的医嘱签发失败问题
// 🔧 BugFix#328: 签发时将收费项目状态从草稿改为待收费(文字医嘱跳过)
Long chargeItemId = adviceSaveDto.getChargeItemId();
if (adviceSaveDto.getAdviceType() != null && adviceSaveDto.getAdviceType() == ItemType.TEXT.getValue()) {
// 文字医嘱无费用项,跳过计费状态更新
continue;
}
ChargeItem existingChargeItem = null;
// 方式1通过chargeItemId直接查询

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, null);
adviceDefinitionIdParamList, organizationId, pageNo, pageSize, pricingFlag, List.of(1, 2, 3), null, 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, null).getRecords().get(0);
null, null, organizationId, 1, 1, Whether.NO.getValue(), List.of(3), null, null, null).getRecords().get(0);
if (activityAdviceBaseDto != null) {
// 费用定价
AdvicePriceDto advicePriceDto = activityAdviceBaseDto.getPriceList().get(0);

View File

@@ -48,12 +48,13 @@ public class DoctorStationAdviceController {
@RequestParam(value = "locationId", required = false) Long locationId,
@RequestParam(value = "adviceDefinitionIdParamList", required = false) List<Long> adviceDefinitionIdParamList,
@RequestParam(value = "organizationId", required = false) Long organizationId,
@RequestParam(value = "adviceTypes", defaultValue = "1,2,3") List<Integer> adviceTypes,
@RequestParam(value = "adviceTypes", defaultValue = "1,2,3,8") List<Integer> adviceTypes,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(value = "categoryCode", required = false) String categoryCode) {
@RequestParam(value = "categoryCode", required = false) String categoryCode,
@RequestParam(value = "dischargeFlag", required = false) Integer dischargeFlag) {
return R.ok(iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, searchKey, locationId,
adviceDefinitionIdParamList, organizationId, pageNo, pageSize, null, adviceTypes, null, categoryCode));
adviceDefinitionIdParamList, organizationId, pageNo, pageSize, null, adviceTypes, null, categoryCode, dischargeFlag));
}
/**

View File

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

View File

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

View File

@@ -806,7 +806,7 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
// 医嘱详细信息
Long orgId = SecurityUtils.getLoginUser().getOrgId();
List<AdviceBaseDto> medicationInfos = doctorStationAdviceAppService.getAdviceBaseInfo(null, null, null,
medicationDefinitionIdList, orgId, 1, 500, Whether.NO.getValue(), List.of(1), null, null).getRecords();
medicationDefinitionIdList, orgId, 1, 500, Whether.NO.getValue(), List.of(1), null, null, null).getRecords();
// 当前时间
Date curDate = new Date();
@@ -1094,7 +1094,7 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
// 医嘱详细信息
Long orgId = SecurityUtils.getLoginUser().getOrgId();
List<AdviceBaseDto> activityInfos = doctorStationAdviceAppService.getAdviceBaseInfo(null, null, null,
activityDefinitionIdList, orgId, 1, 500, Whether.NO.getValue(), List.of(3), null, null).getRecords();
activityDefinitionIdList, orgId, 1, 500, Whether.NO.getValue(), List.of(3), null, null, null).getRecords();
// 当前时间
Date curDate = new Date();
@@ -1251,7 +1251,7 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
// 耗材医嘱详细信息
List<AdviceBaseDto> deviceInfos = doctorStationAdviceAppService
.getAdviceBaseInfo(null, null, null, deviceIds, orgId, 1, 500, Whether.NO.getValue(), List.of(2),
null, null)
null, null, null)
.getRecords();
DeviceRequest deviceRequest;

View File

@@ -205,7 +205,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, null).getRecords();
activityDefinitionIdList, 0L, 1, 500, Whether.NO.getValue(), List.of(3), orderPricing, null, null).getRecords();
// 计费
ChargeItem chargeItem;
@@ -299,7 +299,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, null).getRecords();
activityDefinitionIdList, 0L, 1, 500, Whether.NO.getValue(), List.of(3), orderPricing, null, null).getRecords();
// 计费
ChargeItem chargeItem;
for (AutoRollBasicServiceDto autoRollBasicServiceDto : autoRollBasicService) {

View File

@@ -2329,7 +2329,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, null).getRecords().get(0);
null, null, organizationId, 1, 1, Whether.NO.getValue(), List.of(1, 2, 3), null, null, null).getRecords().get(0);
// 价格信息
if (activityAdviceBaseDto != null) {
// 费用定价

View File

@@ -198,7 +198,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, null).getRecords();
organizationId, 1, 100, Whether.NO.getValue(), List.of(1, 2, 3), null, null, null).getRecords();
// 创建AdviceBaseDto的映射以adviceDefinitionId为key
Map<Long, AdviceBaseDto> adviceMap = personalRecords.stream().collect(Collectors
.toMap(AdviceBaseDto::getAdviceDefinitionId, advice -> advice, (existing, replacement) -> existing // 如果有重复key保留第一个
@@ -249,7 +249,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, null).getRecords();
organizationId, 1, 100, Whether.NO.getValue(), List.of(1, 2, 3), null, null, null).getRecords();
// 创建AdviceBaseDto的映射以adviceDefinitionId为key
Map<Long, AdviceBaseDto> adviceMap = personalRecords.stream().collect(Collectors
.toMap(AdviceBaseDto::getAdviceDefinitionId, advice -> advice, (existing, replacement) -> existing // 如果有重复key保留第一个
@@ -298,7 +298,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, null).getRecords();
organizationId, 1, 100, Whether.NO.getValue(), List.of(1, 2, 3), null, 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

@@ -192,16 +192,18 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
// 药品
List<RegAdviceSaveDto> medicineList = regAdviceSaveList.stream()
.filter(e -> ItemType.MEDICINE.getValue().equals(e.getAdviceType())).collect(Collectors.toList());
// 诊疗活动包含护理adviceType=26、手术adviceType=6
// 诊疗活动包含护理adviceType=26、手术adviceType=6、文字医嘱adviceType=8
List<RegAdviceSaveDto> activityList = regAdviceSaveList.stream()
.filter(e -> ItemType.ACTIVITY.getValue().equals(e.getAdviceType())
|| ItemType.SURGERY.getValue().equals(e.getAdviceType())
|| ItemType.TEXT.getValue().equals(e.getAdviceType())
|| (e.getAdviceType() != null && e.getAdviceType() == 26))
.collect(Collectors.toList());
// 耗材 🔧 Bug #147 修复
List<RegAdviceSaveDto> deviceList = regAdviceSaveList.stream()
.filter(e -> ItemType.DEVICE.getValue().equals(e.getAdviceType())).collect(Collectors.toList());
// 保存时,校验临时医嘱库存
if (AdviceOpType.SAVE_ADVICE.getCode().equals(adviceOpType)) {
List<RegAdviceSaveDto> medUpdateList
@@ -214,7 +216,8 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
List<RegAdviceSaveDto> tempCheckList = regAdviceSaveList.stream()
.filter(e -> TherapyTimeType.TEMPORARY.getValue().equals(e.getTherapyEnum())
&& !DbOpType.DELETE.getCode().equals(e.getDbOpType())
&& !ItemType.ACTIVITY.getValue().equals(e.getAdviceType()))
&& !ItemType.ACTIVITY.getValue().equals(e.getAdviceType())
&& !ItemType.TEXT.getValue().equals(e.getAdviceType())) // 文字医嘱跳过库存校验
.collect(Collectors.toList());
List<AdviceSaveDto> needCheckList = new ArrayList<>(tempCheckList);
// 校验库存
@@ -627,6 +630,7 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
// 签发操作
boolean is_sign = AdviceOpType.SIGN_ADVICE.getCode().equals(adviceOpType);
// 收集已处理的requestId用于批量更新状态
List<Long> processedRequestIds = new ArrayList<>();
@@ -708,7 +712,12 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
tempServiceRequest.setUnitCode(regAdviceSaveDto.getUnitCode()); // 请求单位编码
tempServiceRequest.setCategoryEnum(regAdviceSaveDto.getCategoryEnum()); // 请求类型
tempServiceRequest.setTherapyEnum(regAdviceSaveDto.getTherapyEnum()); // 治疗类型,临时(需要前端传)
tempServiceRequest.setActivityId(regAdviceSaveDto.getAdviceDefinitionId());// 诊疗定义id
// 文字医嘱(type=8)不走定价体系activityId设置为0L占位
if (ItemType.TEXT.getValue().equals(regAdviceSaveDto.getAdviceType())) {
tempServiceRequest.setActivityId(0L);
} else {
tempServiceRequest.setActivityId(regAdviceSaveDto.getAdviceDefinitionId());// 诊疗定义id
}
tempServiceRequest.setPatientId(regAdviceSaveDto.getPatientId()); // 患者
tempServiceRequest.setRequesterId(regAdviceSaveDto.getPractitionerId()); // 开方医生
tempServiceRequest.setEncounterId(regAdviceSaveDto.getEncounterId()); // 就诊id
@@ -731,8 +740,8 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
processedRequestIds.add(tempServiceRequest.getId());
}
// 保存时,保存诊疗费用项
if (is_save) {
// 保存时,保存诊疗费用项(文字医嘱跳过计费)
if (is_save && !ItemType.TEXT.getValue().equals(regAdviceSaveDto.getAdviceType())) {
chargeItem = new ChargeItem();
chargeItem.setId(regAdviceSaveDto.getChargeItemId()); // 费用项id
chargeItem.setStatusEnum(ChargeItemStatus.DRAFT.getValue()); // 收费状态
@@ -1034,9 +1043,10 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
.filter(e -> ItemType.MEDICINE.getValue().equals(e.getAdviceType())).collect(Collectors.toList());
List<Long> medicineRequestIds
= medicineList.stream().map(AdviceBatchOpParam::getRequestId).collect(Collectors.toList());
// 诊疗包含护理adviceType=26
// 诊疗包含护理adviceType=26、文字医嘱adviceType=8
List<AdviceBatchOpParam> activityList = paramList.stream()
.filter(e -> ItemType.ACTIVITY.getValue().equals(e.getAdviceType())
|| ItemType.TEXT.getValue().equals(e.getAdviceType())
|| (e.getAdviceType() != null && e.getAdviceType() == 26))
.collect(Collectors.toList());
List<Long> activityRequestIds
@@ -1100,9 +1110,10 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
.filter(e -> ItemType.MEDICINE.getValue().equals(e.getAdviceType())).collect(Collectors.toList());
List<Long> medicineRequestIds
= medicineList.stream().map(AdviceBatchOpParam::getRequestId).collect(Collectors.toList());
// 诊疗包含护理adviceType=26
// 诊疗包含护理adviceType=26、文字医嘱adviceType=8
List<AdviceBatchOpParam> activityList = paramList.stream()
.filter(e -> ItemType.ACTIVITY.getValue().equals(e.getAdviceType())
|| ItemType.TEXT.getValue().equals(e.getAdviceType())
|| (e.getAdviceType() != null && e.getAdviceType() == 26))
.collect(Collectors.toList());
List<Long> activityRequestIds
@@ -1144,9 +1155,10 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
.filter(e -> ItemType.MEDICINE.getValue().equals(e.getAdviceType())).collect(Collectors.toList());
List<Long> medicineRequestIds
= medicineList.stream().map(AdviceBatchOpParam::getRequestId).collect(Collectors.toList());
// 诊疗包含护理adviceType=26
// 诊疗包含护理adviceType=26、文字医嘱adviceType=8
List<AdviceBatchOpParam> activityList = paramList.stream()
.filter(e -> ItemType.ACTIVITY.getValue().equals(e.getAdviceType())
|| ItemType.TEXT.getValue().equals(e.getAdviceType())
|| (e.getAdviceType() != null && e.getAdviceType() == 26))
.collect(Collectors.toList());
List<Long> activityRequestIds

View File

@@ -162,7 +162,7 @@ public class SpecialAdviceAppServiceImpl implements ISpecialAdviceAppService {
adviceBaseDto.setAdviceDefinitionId(definitionId); // 医嘱定义id
// 对应的诊疗医嘱信息
activityAdviceBaseDto = iDoctorStationAdviceAppService.getAdviceBaseInfo(adviceBaseDto, null, null,
null, null, 1, 1, null, List.of(3), null, null).getRecords().get(0);
null, null, 1, 1, null, List.of(3), null, null, null).getRecords().get(0);
// 逻辑1---------------------直接新增
longServiceRequest.setStatusEnum(RequestStatus.DRAFT.getValue());// 请求状态
longServiceRequest.setOccurrenceStartTime(startTime); // 医嘱开始时间
@@ -209,7 +209,7 @@ public class SpecialAdviceAppServiceImpl implements ISpecialAdviceAppService {
adviceBaseDto.setAdviceDefinitionId(definitionId); // 医嘱定义id
// 对应的诊疗医嘱信息
activityAdviceBaseDto = iDoctorStationAdviceAppService
.getAdviceBaseInfo(adviceBaseDto, null, null, null, null, 1, 1, null, List.of(3), null, null)
.getAdviceBaseInfo(adviceBaseDto, null, null, null, null, 1, 1, null, List.of(3), null, null, null)
.getRecords().get(0);
longServiceRequest.setStatusEnum(RequestStatus.DRAFT.getValue());// 请求状态
@@ -349,7 +349,7 @@ public class SpecialAdviceAppServiceImpl implements ISpecialAdviceAppService {
adviceBaseDto.setAdviceDefinitionId(transferOrganizationDefinitionId); // 医嘱定义id
// 转科的医嘱信息
AdviceBaseDto activityAdviceBaseDto = iDoctorStationAdviceAppService
.getAdviceBaseInfo(adviceBaseDto, null, null, null, null, 1, 1, null, List.of(3), null, null)
.getAdviceBaseInfo(adviceBaseDto, null, null, null, null, 1, 1, null, List.of(3), null, null, null)
.getRecords().get(0);
// 保存转科医嘱请求
ServiceRequest serviceRequest = new ServiceRequest();
@@ -431,7 +431,7 @@ public class SpecialAdviceAppServiceImpl implements ISpecialAdviceAppService {
// 出院的医嘱信息
List<AdviceBaseDto> adviceList = iDoctorStationAdviceAppService.getAdviceBaseInfo(null, null, null,
List.of(transferOrganizationDefinitionId), null, 1, 1, null, List.of(3), null, null).getRecords();
List.of(transferOrganizationDefinitionId), null, 1, 1, null, List.of(3), null, null, null).getRecords();
if (CollectionUtils.isEmpty(adviceList)) {
return R.fail("未找到出院医嘱定义数据,请确认诊疗目录中已配置出院医嘱");
}

View File

@@ -128,6 +128,15 @@
<if test="medicationTableName != null">
AND (T5.instance_table = #{medicationTableName} OR T5.instance_table IS NULL)
</if>
<if test="dischargeFlag != null and dischargeFlag == 1">
AND t1.category_code IN ('1', '2')
AND t1.inject_flag = 0
AND t1.id NOT IN (
SELECT DISTINCT medication_def_id FROM med_medication
WHERE delete_flag = '0'
AND (dose_form_code = '4' OR method_code IN ('4','401','402','403','404','405'))
)
</if>
)
<if test="adviceTypes.contains(2) or adviceTypes.contains(3)">UNION ALL</if>
</if>
@@ -721,7 +730,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 3 END AS advice_type,
(SELECT CASE WHEN T1.category_enum = 4 THEN 6 WHEN T1.category_enum = 8 THEN 8 ELSE 3 END AS advice_type,
T1.id AS request_id,
T1.id || '-3' AS unique_key,
'' AS prescription_no,

View File

@@ -296,7 +296,7 @@
AND T1.refund_device_id IS NULL
ORDER BY T1.status_enum)
UNION ALL
(SELECT CASE WHEN T1.category_enum IN (4, 24) THEN 6 ELSE 3 END AS advice_type,
(SELECT CASE WHEN T1.category_enum IN (4, 24) THEN 6 WHEN T1.category_enum = 8 THEN 8 ELSE 3 END AS advice_type,
T1.id AS request_id,
T1.id || '-3' AS unique_key,
T1.requester_id AS requester_id,
@@ -306,7 +306,7 @@
null AS skin_test_flag,
null AS inject_flag,
null AS group_id,
COALESCE(T2.NAME, T1.content_json::jsonb->>'surgeryName') AS advice_name,
COALESCE(T2.NAME, T1.content_json::jsonb->>'surgeryName', T1.content_json::jsonb->>'adviceName') AS advice_name,
'' AS volume,
'' AS lot_number,
T1.quantity AS quantity,

View File

@@ -35,7 +35,12 @@ public enum ItemType implements HisEnumInterface {
/**
* 手术前端使用值6避免与耗材冲突
*/
SURGERY(6, "6", "手术");
SURGERY(6, "6", "手术"),
/**
* 文字医嘱
*/
TEXT(8, "8", "文字");
@EnumValue
private Integer value;

View File

@@ -11,8 +11,8 @@
:height="computedTableHeight"
:row-config="{ keyField: rowKey || 'id', isHover: true }"
:highlight-current-row="highlightCurrentRow"
:show-overflow="true"
:show-header-overflow="title"
show-overflow="title"
show-header-overflow="title"
:auto-resize="true"
:scroll-x="{ enabled: true, gt: 20 }"
:scroll-y="{ enabled: true, gt: 50 }"

View File

@@ -87,21 +87,21 @@
title="单据号"
align="center"
min-width="90"
show-overflow
show-overflow="title"
/>
<vxe-column
field="applicantName"
title="申请人"
align="center"
min-width="65"
show-overflow
show-overflow="title"
/>
<vxe-column
field="locationName"
title="发药药房"
align="center"
min-width="75"
show-overflow
show-overflow="title"
/>
<vxe-column
field="statusEnum_enumText"
@@ -217,7 +217,7 @@
field="manufacturerText"
title="生产厂家"
min-width="120"
:show-overflow="true"
show-overflow="title"
>
<template #default="scope">
{{ scope.row.manufacturerText || '-' }}

View File

@@ -89,12 +89,15 @@ function refresh(adviceType: any, categoryCode: string, searchKey: string) {
// 有搜索词时跨类型搜索,避免用户输入"级护理"但因当前adviceType为药品而搜不到诊疗类护理项目
if (searchKey) {
queryParams.value.adviceTypes = [1, 2, 3, 6];
queryParams.value.dischargeFlag = undefined;
} else if (adviceType == 7) {
// Bug #589: 出院带药仅检索西药和中成药
queryParams.value.adviceTypes = [1];
queryParams.value.dischargeFlag = 1;
} else {
queryParams.value.adviceTypes =
adviceType !== undefined && adviceType !== '' ? [parseInt(adviceType)] : [1, 2, 3, 6];
queryParams.value.dischargeFlag = undefined;
}
queryParams.value.categoryCode = categoryCode || '';
queryParams.value.searchKey = searchKey || '';
@@ -120,8 +123,8 @@ function getList() {
// 药品/耗材需要有库存才显示,诊疗/手术直接显示
adviceBaseList.value = records.filter((item: any) => {
// Bug #589: 出院带药仅显示西药(categoryCode='2')和中成药(categoryCode='1')
if (queryParams.value.adviceTypes.length === 1 && queryParams.value.adviceTypes[0] === 1 && !queryParams.value.categoryCode) {
// Bug #589: 出院带药仅显示西药(categoryCode='2')和中成药(categoryCode='1'),后端已过滤注射类
if (queryParams.value.dischargeFlag === 1) {
if (item.adviceType == 1 && (item.categoryCode == '1' || item.categoryCode == '2')) {
return handleQuantity(item) !== '0';
}

View File

@@ -111,33 +111,6 @@
/>
</template>
</el-select>
<span>=</span>
<!-- 单次剂量 -->
<el-form-item prop="dose" class="required-field" data-prop="dose" label-width="0">
<el-input-number
v-model="row.dose"
controls-position="right"
:controls="false"
style="width: 70px; margin-left: 32px"
:ref="(el) => setInputRef('dose', el)"
@input="() => { convertDoseValues(); calculateTotalAmount(); }"
@keyup.enter.prevent="handleEnter('dose')"
/>
</el-form-item>
<!-- 全部单位 -->
<el-select
v-model="row.doseUnitCode"
style="width: 70px"
placeholder=" "
@change="() => { convertValues(); calculateTotalAmount(); }"
>
<el-option
v-for="item in row.unitCodeList"
:value="item.value"
:label="item.label"
:key="item.value"
/>
</el-select>
</div>
<div class="form-group">
<el-form-item
@@ -409,6 +382,66 @@
</div>
</div>
</template>
<!-- 🔧 文字医嘱面板 (adviceType=8) -->
<template v-else-if="row.adviceType == 8">
<div style="display: flex; align-items: center; margin-bottom: 16px; gap: 16px">
<span class="medicine-title">文字医嘱</span>
<el-form-item label="开始时间:" prop="startTime" class="required-field" data-prop="startTime">
<el-date-picker
v-model="row.startTime"
type="datetime"
placeholder="选择开始时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
style="width: 190px; margin-right: 12px"
/>
</el-form-item>
<el-form-item label="频次:" prop="rateCode">
<el-select v-model="row.rateCode" placeholder="频次" style="width: 120px" filterable clearable>
<el-option
v-for="dict in config.rateCode"
:key="dict.value"
:label="dict.value + ' ' + dict.label"
:value="dict.value"
@click="() => (row.rateCode_dictText = dict.value + ' ' + dict.label)"
/>
</el-select>
</el-form-item>
<el-form-item label="执行科室:" prop="orgId">
<el-tree-select
clearable
v-model="row.orgId"
style="width: 200px"
:data="orgTreeData"
:props="{ value: 'id', label: 'name', children: 'children' }"
value-key="id"
check-strictly
default-expand-all
:fallback-option="orgFallbackOption"
@change="handleOrgChange"
placeholder="请选择执行科室"
/>
</el-form-item>
<span class="total-amount">-</span>
</div>
<div style="display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap">
<el-form-item label="文字内容:" prop="adviceName" class="required-field" data-prop="adviceName" style="flex: 1; min-width: 400px">
<el-input
v-model="row.adviceName"
type="textarea"
:rows="3"
placeholder="请输入文字医嘱内容3~50字"
maxlength="50"
show-word-limit
style="width: 100%"
/>
</el-form-item>
<div class="form-actions" style="align-self: flex-end">
<el-button type="primary" @click="handleSave">确定</el-button>
<el-button @click="handleCancel">取消</el-button>
</div>
</div>
</template>
<template v-else-if="row.adviceType == 2">
<div style="display: flex; align-items: center; margin-bottom: 16px; gap: 16px">
<span style="font-size: 16px; font-weight: 600">
@@ -650,6 +683,7 @@ onMounted(() => {
registerFormRef();
});
// Bug #615: 临时医嘱频次默认改为 ONCE临时一次不再强制设为 ST
// 文字医嘱(type=8)临时医嘱默认频次为 ST立即
if (props.row.therapyEnum == '2' && !props.row.rateCode && props.row.adviceType != 7) {
setDefaultRateCode();
}
@@ -659,6 +693,7 @@ watch(
() => props.row.therapyEnum,
(newVal) => {
// Bug #615: 临时医嘱频次仅在字段为空时给默认值,允许医生自主修改
// 文字医嘱(type=8)临时医嘱默认频次为 ST立即
if (newVal == '2' && !props.row.rateCode && props.row.adviceType != 7) {
setDefaultRateCode();
} else if (newVal == '1') {
@@ -668,8 +703,30 @@ watch(
}
);
// 文字医嘱(type=8):选择类型后强制设置频次为 ST立即覆盖之前的默认值
watch(
() => props.row.adviceType,
(newVal) => {
if (newVal == 8 && props.row.therapyEnum == '2') {
setDefaultRateCode();
}
}
);
const setDefaultRateCode = () => {
if (Array.isArray(props.config.rateCode)) {
// 文字医嘱(type=8):默认频次为 ST立即
if (props.row.adviceType == 8) {
const stOption = props.config.rateCode.find((item) => item.value === 'ST');
if (stOption) {
props.row.rateCode = 'ST';
props.row.rateCode_dictText = 'ST ' + stOption.label;
} else {
props.row.rateCode = 'ST';
props.row.rateCode_dictText = 'ST 立即';
}
return;
}
// 临时医嘱默认频次:优先选 ONCE临时一次其次 ST立即
const onceOption = props.config.rateCode.find((item) => item.value === 'ONCE');
if (onceOption) {
@@ -728,6 +785,15 @@ const handleSave = () => {
console.error('Form ref not found');
return;
}
// 出院带药用药天数校验1-7天
if (props.row.adviceType == 7) {
if (!props.row.dispensePerDuration || props.row.dispensePerDuration < 1 || props.row.dispensePerDuration > 7) {
if (proxy?.$modal?.msgWarning) {
proxy.$modal.msgWarning('出院带药用药天数需在1-7天内');
}
return;
}
}
formRef.value.validate((valid: boolean) => {
if (valid) {
emit('save', props.row, props.index);
@@ -807,9 +873,8 @@ const calculateTotalAmount = () => {
row.quantity = new Decimal(row.doseQuantity || 0).mul(row.dispensePerDuration || 0).toNumber();
}
const qty = new Decimal(row.quantity || row.doseQuantity || 0);
// 根据首次用量单位类型决定使用哪个单价
const unitType = row.unitCodeList?.find((k) => k.value == row.doseUnitCode)?.type;
const price = unitType == 'unit' ? row.unitPrice : row.minUnitPrice;
// 统一使用小单位单价计算
const price = row.minUnitPrice;
const roundedPrice = new Decimal(price || 0).toDecimalPlaces(2, Decimal.ROUND_HALF_UP);
row.totalPrice = qty.mul(roundedPrice).toDecimalPlaces(2, Decimal.ROUND_HALF_UP).toString();
});

View File

@@ -97,7 +97,7 @@
height="100%"
ref="prescriptionRef"
:data="filterPrescriptionList"
:row-config="{ keyField: 'uniqueKey' }" :expand-config="{ trigger: 'row', expandRowKeys: expandOrder }"
:row-config="{ keyField: 'uniqueKey' }" :expand-config="{ expandRowKeys: expandOrder }"
border
@cell-click="clickRow"
@cell-dblclick="clickRowDb"
@@ -121,22 +121,24 @@
<vxe-column type="checkbox" align="center" width="60" />
<vxe-column title="组" align="center" width="60" field="groupIcon" />
<vxe-column title="类型" align="center" field="" width="120">
<vxe-column title="类型" align="center" field="" width="140">
<template #default="scope">
<el-radio-group
v-model="scope.row.therapyEnum"
size="small"
v-if="getRowDisabled(scope.row) && !scope.row.dischargeFlag"
@change="(val) => handleTherapyChange(val, scope.row)"
v-if="getRowDisabled(scope.row)"
:disabled="scope.row.dischargeFlag"
@click.stop
style="white-space: nowrap"
>
<el-radio-button>长期</el-radio-button>
<el-radio-button value="1">长期</el-radio-button>
<el-radio-button value="2">临时</el-radio-button>
</el-radio-group>
<span
v-else
:style="scope.row.dischargeFlag ? 'color: #F59E0B' : (scope.row.therapyEnum == '1' ? 'color: #a6745c' : 'color: #3787a5')"
:style="scope.row.therapyEnum == '1' ? 'color: #a6745c' : 'color: #3787a5'"
>
{{ scope.row.dischargeFlag ? '临时(出院带药)' : (scope.row.therapyEnum === '1' ? '长期' : '临时') }}
{{ scope.row.therapyEnum === '1' ? '长期' : '临时' }}
</span>
</template>
</vxe-column>
@@ -145,9 +147,19 @@
{{ scope.row.createdStaffName || '-' }}
</template>
</vxe-column>
<vxe-column title="开始时间" align="center" field="startTime" width="170">
<vxe-column title="开始时间" align="center" field="startTime" width="200">
<template #default="scope">
<span v-if="!scope.row.isEdit">
<el-date-picker
v-if="scope.row.isEdit"
v-model="scope.row.startTime"
type="datetime"
placeholder="选择开始时间"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
size="small"
style="width: 100%"
/>
<span v-else>
{{ scope.row.startTime || '-' }}
</span>
</template>
@@ -162,7 +174,6 @@
:disabled="!isCategoryLoaded"
@change="
(value) => {
expandOrder = [];
filterPrescriptionList[scope.rowIndex].adviceName = undefined;
// 根据选中的医嘱类型,设置对应的 categoryCode
const selectedItem = adviceTypeList.find(item => item.value === value);
@@ -182,13 +193,17 @@
adviceQueryParams.value = {
adviceType: newAdviceType,
categoryCode: newCategoryCode,
searchKey: adviceQueryParams.value.searchKey || '',
searchKey: adviceQueryParams.value?.searchKey || '',
};
// 直接调用子组件 refresh 方法,立即刷新列表(用 scope.rowIndex 找到当前行的子组件)
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[scope.rowIndex] : adviceTableRef.value;
if (tableRef && tableRef.refresh) {
tableRef.refresh(newAdviceType, newCategoryCode, '');
}
// 文字医嘱没有药品选择步骤,选类型后直接展开编辑行
if (newAdviceType == 8) {
expandTextRow(scope.rowIndex);
}
}
"
clearable
@@ -341,7 +356,7 @@
</span>
</template>
</vxe-column>
<vxe-column title="备注" align="center" field="remark" width="150" show-overflow>
<vxe-column title="备注" align="center" field="remark" width="150" show-overflow="title">
<template #default="scope">
<span v-if="!scope.row.isEdit">
{{ scope.row.remark || '-' }}
@@ -468,6 +483,8 @@ const encounterDiagnosisId = ref('');
const diagnosisName = ref('');
const diagnosisInfo = ref({});
const loading = ref(false);
// Bug #587: 标记弹窗刚被关闭的行uniqueKey防止关闭弹窗时误触发行展开
const popoverJustClosedByKey = ref(null);
// 停嘱弹窗
const stopDialogVisible = ref(false);
@@ -486,6 +503,11 @@ const rowRules = ref({
rateCode: [{ required: true, message: '请选择频次', trigger: 'change' }],
methodCode: [{ required: true, message: '请选择给药途径', trigger: 'change' }],
orgId: [{ required: true, message: '请选择执行科室', trigger: 'change' }],
adviceName: [
{ required: true, message: '请输入文字医嘱内容', trigger: 'blur' },
{ min: 3, max: 50, message: '文字医嘱内容长度为3~50个字符', trigger: 'blur' },
],
startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
});
const unitMap = ref({
dose: 'dose',
@@ -562,6 +584,7 @@ const adviceTypeList = computed(() => {
{ label: '诊疗', value: 3, adviceType: 3, categoryCode: '' },
{ label: '手术', value: 6, adviceType: 6, categoryCode: '' },
{ label: '出院带药', value: 7, adviceType: 7, categoryCode: '' },
{ label: '文字', value: 8, adviceType: 8, categoryCode: '' },
{ label: '全部', value: '', adviceType: '', categoryCode: '' },
];
}
@@ -592,6 +615,9 @@ const adviceTypeList = computed(() => {
// 始终添加"出院带药"选项(不需要取药科室配置)
typeList.push({ label: '出院带药', value: 7, adviceType: 7, categoryCode: '' });
// 始终添加"文字"选项(不需要取药科室配置)
typeList.push({ label: '文字', value: 8, adviceType: 8, categoryCode: '' });
// 添加全部选项
typeList.push({ label: '全部', value: '', adviceType: '', categoryCode: '' });
@@ -632,8 +658,7 @@ watch(
nextTick(() => {
const index = prescriptionList.value.findIndex((row) => row.uniqueKey === newValue[0]);
const items = proxy.$refs['formRef' + index]?.$el?.querySelectorAll('[data-prop]');
requiredProps.value = Array.from(items).map((item) => item.dataset.prop);
console.log(requiredProps.value, 'requiredProps.value');
requiredProps.value = items ? Array.from(items).map((item) => item.dataset.prop) : {};
});
} else {
requiredProps.value = {};
@@ -677,7 +702,7 @@ function getListInfo(addNewRow) {
loadingInstance.close();
}, 180);
isAdding.value = false;
expandOrder.value = [];
collapseAllExpanded();
// 🔧 修复:先加载科室树,再处理处方数据
// 确保 resolveOrgId 在 organization 树已加载的情况下执行
// 避免因树为空导致 orgId 无法匹配,从而显示数字 ID 而非中文名称
@@ -886,7 +911,7 @@ function handleAddPrescription() {
showPopover: false,
isEdit: true,
statusEnum: 1,
therapyEnum: '1', // 默认为长期医嘱
therapyEnum: '2', // 默认为临时医嘱
startTime: defaultStartTime,
});
getGroupMarkers();
@@ -946,6 +971,12 @@ function clickRowDb({ row, column, event }) {
if (event && event.target.closest('.el-checkbox')) {
return; // 如果是复选框点击,不执行行点击逻辑
}
// Bug #587: 关闭弹窗的同一点击不应触发行展开,标记仅生效一次
if (popoverJustClosedByKey.value === row.uniqueKey) {
popoverJustClosedByKey.value = null;
return;
}
popoverJustClosedByKey.value = null;
if (expandOrder.value.length > 0) {
proxy.$modal.msgWarning('有医嘱处于编辑状态,请先保存,再编辑此条医嘱');
return;
@@ -1027,11 +1058,29 @@ function handleDiagnosisChange(item) {
encounterDiagnosisId.value = item.encounterDiagnosisId;
}
// 文字医嘱选类型后展开编辑行(模板中无法直接用 nextTick
function expandTextRow(rowIndex) {
const row = filterPrescriptionList.value[rowIndex];
if (!row) return;
expandOrder.value = [row.uniqueKey];
nextTick(() => {
if (prescriptionRef.value?.setRowExpand) {
prescriptionRef.value.setRowExpand([row], true);
}
});
}
function handleFocus(row, index) {
rowIndex.value = index;
// 文字医嘱(type=8)不弹药品搜索框,直接展开填写面板
const adviceType = row.adviceType !== undefined ? row.adviceType : adviceQueryParams.value.adviceType;
if (adviceType == 8) {
row.showPopover = false;
return;
}
row.showPopover = true;
// Bug #555: handleFocus 初始化查询参数并加载初始数据searchKey 为空,无竞态风险)
const adviceType = row.adviceType !== undefined ? row.adviceType : adviceQueryParams.value.adviceType;
let categoryCode = '';
if (row.adviceType !== undefined) {
const selectValue = (adviceType == 1 && row.categoryCode) ? '1-' + row.categoryCode : adviceType;
@@ -1048,14 +1097,21 @@ function handleFocus(row, index) {
function handleBlur(row) {
row.showPopover = false;
// Bug #587: 标记弹窗刚关闭,防止点击空白处时触发行展开
popoverJustClosedByKey.value = row.uniqueKey;
}
function handleChange(value) {
adviceQueryParams.value.searchKey = value;
// @focus 已先于 @input 执行rowIndex 必定有效
// 文字医嘱(type=8)不触发药品搜索
const currentIndex = rowIndex.value;
if (currentIndex < 0) return;
const row = filterPrescriptionList.value[currentIndex];
const adviceType = row?.adviceType !== undefined ? row.adviceType : adviceQueryParams.value.adviceType;
if (adviceType == 8) {
return;
}
adviceQueryParams.value.searchKey = value;
// @focus 已先于 @input 执行rowIndex 必定有效
// popover 被 blur 关闭后,用户继续输入时自行打开
if (!row.showPopover) {
row.showPopover = true;
@@ -1077,7 +1133,6 @@ function handleChange(value) {
* 选择药品回调
*/
function selectAdviceBase(key, row) {
console.log('row===========>', JSON.stringify(row));
// 每次选择药品时,将当前行数据初始化为最初状态
const currentUniqueKey = prescriptionList.value[rowIndex.value]?.uniqueKey || key;
@@ -1086,12 +1141,18 @@ function selectAdviceBase(key, row) {
uniqueKey: currentUniqueKey,
isEdit: true,
statusEnum: 1,
showPopover: false, // Bug #587: 选择药品后关闭弹窗
therapyEnum: prevRow.therapyEnum,
startTime: prevRow.startTime || defaultStartTimeFn(), // Bug #587: 保留开始时间
// Bug #589: 出院带药需要保留类型和标志setValue中可能被API数据覆盖
adviceType: prevRow.adviceType || undefined,
dischargeFlag: prevRow.dischargeFlag || undefined,
};
setValue(row);
try {
setValue(row);
} catch (e) {
console.warn('setValue error:', e);
}
// Bug #589: 出院带药选择药品后恢复类型标志
if (prescriptionList.value[rowIndex.value]?.dischargeFlag) {
prescriptionList.value[rowIndex.value].adviceType = 7;
@@ -1132,7 +1193,13 @@ function selectAdviceBase(key, row) {
expandOrderAndFocus(currentUniqueKey, row);
});
} else {
// 不是皮试药品,直接展开订单
// 选完药品后展开编辑区域,供医生编辑剂量等字段
expandOrder.value = [currentUniqueKey];
// 直接调 vxe-table 实例方法展开expandRowKeys 仅在初始化时生效)
const rowObj = filterPrescriptionList.value.find(item => item.uniqueKey === currentUniqueKey);
if (rowObj && prescriptionRef.value?.setRowExpand) {
prescriptionRef.value.setRowExpand([rowObj], true);
}
expandOrderAndFocus(currentUniqueKey, row);
}
}
@@ -1141,25 +1208,18 @@ function selectAdviceBase(key, row) {
* 展开订单并聚焦输入框
*/
function expandOrderAndFocus(key, row) {
expandOrder.value = [];
// 只聚焦,expandOrder 已在 selectAdviceBase 里设好
nextTick(() => {
nextTick(() => {
expandOrder.value = [key];
const tableData = filterPrescriptionList.value;
const targetRow = tableData.find((item) => item.uniqueKey === key);
if (targetRow && prescriptionRef.value) {
prescriptionRef.value.toggleRowExpansion(targetRow, true);
}
if (row.adviceType == 1) {
if (row.injectFlag == 1) {
inputRefs.value['executeNum']?.focus();
} else {
inputRefs.value['doseQuantity']?.focus();
}
// 聚焦第一个输入框
if (row.adviceType == 1) {
if (row.injectFlag == 1) {
inputRefs.value['executeNum']?.focus();
} else {
inputRefs.value['quantity']?.focus();
inputRefs.value['doseQuantity']?.focus();
}
});
} else {
inputRefs.value['quantity']?.focus();
}
});
}
@@ -1260,7 +1320,7 @@ function handleDelete() {
});
}
}
expandOrder.value = [];
collapseAllExpanded();
isAdding.value = false;
adviceQueryParams.value.adviceType = undefined;
if (sum === selectRows.length) {
@@ -1322,7 +1382,7 @@ function handleSave() {
if (filterPrescriptionList.value[0].isEdit && !filterPrescriptionList.value[0].adviceType) {
prescriptionList.value.shift();
isAdding.value = false;
expandOrder.value = [];
collapseAllExpanded();
}
const selectedRows = prescriptionRef.value ? prescriptionRef.value.getCheckboxRecords() : [];
@@ -1509,13 +1569,26 @@ function handleOrderBindInfo(bindIdInfo) {
});
}
function collapseAllExpanded() {
expandOrder.value = [];
// VXE Table v4: expandRowKeys 只在初始化生效,必须调实例方法收起行
if (prescriptionRef.value?.clearRowExpand) {
prescriptionRef.value.clearRowExpand();
} else if (prescriptionRef.value?.setRowExpand) {
const allRows = prescriptionRef.value.getData?.() || [];
if (allRows.length > 0) {
prescriptionRef.value.setRowExpand(allRows, false);
}
}
}
function handleCancelEdit(row, index) {
if (isAdding.value && index === 0 && !row.requestId) {
isAdding.value = false;
} else {
row.isEdit = false;
}
expandOrder.value = [];
collapseAllExpanded();
}
function handleSaveSign(row, index) {
@@ -1523,8 +1596,16 @@ function handleSaveSign(row, index) {
if (!validateStartTime(row.startTime)) {
return;
}
// Bug #589: 出院带药用药天数校验1-7天
if (row.adviceType == 7) {
if (!row.dispensePerDuration || row.dispensePerDuration < 1 || row.dispensePerDuration > 7) {
proxy.$modal.msgWarning('出院带药用药天数需在1-7天内');
return;
}
}
// Bug #589: 出院带药按药品逻辑处理绑设备检查
if (row.adviceType != 2) {
// 文字医嘱(type=8)跳过绑设备检查和计费逻辑
if (row.adviceType != 2 && row.adviceType != 8) {
let itemNo = row.adviceType == 1 || row.adviceType == 7 ? row.methodCode : row.adviceDefinitionId;
if (!itemNo) {
console.warn('绑定设备检查跳过itemNo为空adviceType=' + row.adviceType + ', adviceName=' + row.adviceName + '');
@@ -1551,13 +1632,43 @@ function handleSaveSign(row, index) {
// 更新UI状态
row.isEdit = false;
isAdding.value = false;
expandOrder.value = [];
collapseAllExpanded();
// 执行保存
row.contentJson = undefined;
row.patientId = patientInfo.value.patientId;
row.encounterId = patientInfo.value.encounterId;
row.accountId = accountId.value;
// 🔧 文字医嘱(type=8)跳过计费逻辑总金额为0
if (row.adviceType == 8) {
row.totalPrice = 0;
row.unitPrice = 0;
row.quantity = 1;
row.minUnitQuantity = 1;
row.unitCode = row.unitCode || '次';
row.categoryEnum = 8;
row.conditionId = conditionId.value;
row.encounterDiagnosisId = encounterDiagnosisId.value;
row.diagnosisName = diagnosisName.value;
row.therapyEnum = row.therapyEnum || '1';
row.contentJson = JSON.stringify(row);
row.dbOpType = row.requestId ? '2' : '1';
savePrescription({ regAdviceSaveList: [row] }).then((res) => {
if (res.code === 200) {
proxy.$modal.msgSuccess('保存成功');
row.isEdit = false;
isAdding.value = false;
collapseAllExpanded();
if (!row.requestId && isAdding.value && prescriptionList.value[0].adviceName) {
handleAddPrescription();
}
refresh();
}
});
return;
}
// Bug #589: 出院带药(adviceType=7)按药品(adviceType=1)逻辑保存
if (row.adviceType == 1 || row.adviceType == 7) {
row.minUnitQuantity =
@@ -1633,7 +1744,7 @@ function handleSaveBatch() {
if (filterPrescriptionList.value[0].isEdit && !filterPrescriptionList.value[0].adviceType) {
prescriptionList.value.shift();
isAdding.value = false;
expandOrder.value = [];
collapseAllExpanded();
}
// 过滤出未签发的保存
let saveList = filterPrescriptionList.value
@@ -1661,6 +1772,15 @@ function handleSaveBatch() {
parsed.prescriptionCategory = 3;
result.contentJson = JSON.stringify(parsed);
}
// 🔧 文字医嘱(type=8)跳过计费总金额为0
if (result.adviceType == 8) {
result.totalPrice = 0;
result.unitPrice = 0;
result.quantity = result.quantity || 1;
result.minUnitQuantity = result.minUnitQuantity || 1;
result.unitCode = result.unitCode || '次';
result.categoryEnum = 8;
}
return result;
});
if (saveList.length == 0) {
@@ -1721,12 +1841,13 @@ function setValue(row) {
if (row.adviceType != 3) {
// 🔧 Bug #144 修复:检查 inventoryList 是否存在,避免 undefined 错误
if (!row.inventoryList || row.inventoryList.length == 0) {
expandOrder.value = [];
collapseAllExpanded();
proxy.$modal.msgWarning(row.adviceName + '无库存');
return;
}
const priceList = row.priceList || [];
mergedStockList = row.inventoryList.map((item, index) => {
return { ...item, ...row.priceList[index] };
return { ...item, ...priceList[index] };
});
// 获取默认批次号的库存,如果没有让医生重新选
@@ -1978,7 +2099,7 @@ function escKeyListener(e) {
index = prescriptionList.value.findIndex((item) => item.uniqueKey == expandOrder.value[0]);
}
if (index == 0) {
expandOrder.value = [];
collapseAllExpanded();
}
prescriptionList.value.shift();
isAdding.value = false;
@@ -2121,6 +2242,8 @@ function handleStopAdvice() {
return {
requestId: item.requestId,
adviceType: item.adviceType,
startTime: item.startTime,
adviceName: item.adviceName,
};
});
// 默认当前时间
@@ -2144,6 +2267,29 @@ function confirmStopAdvice() {
});
return;
}
// 校验:停嘱时间不能早于患者入院时间
if (patientInfo.value?.inHospitalTime) {
const stopDate = new Date(stopForm.stopTime);
const inHospitalDate = new Date(patientInfo.value.inHospitalTime);
if (stopDate < inHospitalDate) {
const pad = (n) => String(n).padStart(2, '0');
const d = inHospitalDate;
const timeStr = d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes());
ElMessage({ type: 'error', message: '停嘱时间不能早于患者入院时间(' + timeStr + ')' });
return;
}
}
// 校验:停嘱时间不能早于医嘱开始时间
const stopDate = new Date(stopForm.stopTime);
for (const row of pendingStopRows.value) {
if (row.startTime) {
const startDate = new Date(row.startTime);
if (stopDate < startDate) {
ElMessage({ type: 'error', message: '停嘱时间不能早于医嘱【' + (row.adviceName || '') + '】的开始时间(' + row.startTime + ')' });
return;
}
}
}
const requestIdList = pendingStopRows.value.map((item) => ({
...item,
stopTime: stopForm.stopTime,