Compare commits

..

15 Commits

Author SHA1 Message Date
赵云
80695d90f9 Fix Bug #475: 【住院医生工作站】开立检查申请单报错"请先配置当前时间段的执行科室"后,系统仍生成申请记录
根因:saveRequestForm 方法中,执行科室配置校验(activityOrganizationConfig.isEmpty)位于 saveOrUpdate(requestForm) 之后,导致即使校验失败抛出异常,RequestForm 记录已被写入数据库。
修复:将校验逻辑移至方法开头,在任何数据库操作之前执行,确保校验失败时不产生任何脏数据。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 18:12:18 +08:00
关羽
deb6ade97b Fix Bug #472: 住院医生工作站-手术申请单:勾选手术项目无效,导致无法正常开立医嘱
根因分析:surgery.vue 的 el-transfer 组件存在两处与其他正常组件(bloodTransfusion.vue、laboratoryTests.vue)不一致的地方:
1. v-loading 被放置在了 transfer-wrapper 内部的额外 div 上,导致 Element Plus 的加载遮罩层可能与穿梭框交互层产生遮挡冲突
2. applicationList 和 applicationListAll 初始化为 ref([]),而其他组件使用 ref(),导致 Vue 响应式更新时穿梭框内部状态追踪存在差异

修复:
- 将 v-loading 直接放到 transfer-wrapper div 上,去除多余的嵌套 div,与 bloodTransfusion/laboratoryTests 保持一致
- 将 applicationListAll 和 applicationList 的初始化从 ref([]) 改为 ref()

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 18:12:17 +08:00
荀彧
2d7228ca5d Fix Bug #470: 住院医生工作站-手术申请单加载手术项目耗时过长,影响医生开单效率
性能优化:
1. 数据库层: 为 wor_activity_definition 表添加手术项目专用覆盖索引 (idx_wor_activity_def_surgery_covering)
   - 查询从 Index Scan + Filter 改为 Index Only Scan
   - 执行时间从 4.6ms 降至 0.67ms (约7倍提升)
2. Java服务层: 过滤 chargeItemDefinitionIdList 中的 null 值
   - 手术项目无定价定义,原逻辑会将 null 传入批量查询造成浪费
   - 同时跳过 childCharge 批次定价查询(仅药品/耗材需要)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 18:10:44 +08:00
关羽
a447e55d43 Fix Bug #471: 手术管理-门诊手术安排:手术申请查询结果中混入住院检验申请单数据(脏数据)
根因:getSurgeryRequestFormPage 接口构建 RequestFormDto 时 typeCode 传了 null,
导致 SQL 中 type_code 过滤条件被跳过,查出所有申请单(包括 typeCode=22 的住院检验申请单)。
修复:传入 ActivityDefCategory.PROCEDURE.getCode()(值为"24")作为 typeCode,
确保只查询手术类型的申请单。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 18:06:09 +08:00
1be0dc2417 bug515 [住院医生站-临床医嘱] 点击“签发”检验医嘱后系统陷入死循环,Loading无法消失 后端advice_type映射错误 2026-05-13 17:49:27 +08:00
a4b4d36d93 bug512 [住院护士站-汇总发药申请] “全选”开关功能失效,点击后下方医嘱明细未能联动勾选 2026-05-13 17:48:26 +08:00
关羽
940fad5c7d Fix Bug #464: [目录管理-诊疗目录] 新增项目时"零售价"未与"诊疗子项"合计总价自动同步
- calculateTotalPrice: 使用 nextTick 确保 Vue 响应式时序正确,零售价与总价同步
- calculateTotalPrice: 修复判断条件,使用 adviceDefinitionId 而非 retailPrice/childrenRequestNum(可能为0)
- calculateTotalPrice: 使用 Number() 替代 parseFloat/parseInt 统一类型转换
- selectRow: 使用 nextTick 确保 DOM 更新后再计算总价,避免时序问题
- edit/reset: treatmentItems 初始化补充缺失的 name 字段

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 17:21:53 +08:00
关羽
ead3733aac Fix Bug #466: [住院医生工作站-检验申请] 申请单界面缺失核心质控字段(申请类型、标本类型、执行时间)及联动逻辑
在检验申请单弹窗表单中新增三个核心质控字段:
- 申请类型:单选按钮,区分普通/急诊,默认普通
- 标本类型:下拉选择框,支持血液/尿液/痰液等10种标本,默认血液
- 执行时间:日期时间选择器,支持预约未来时间执行

新字段通过 descJson 序列化传递,展示页已支持解析显示。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 17:19:51 +08:00
关羽
28bf385ec2 Fix Bug #463: [目录管理-诊疗目录] 新增/编辑弹窗中"诊疗子项"检索功能失效,无法搜到已维护的项目
根因分析:medicineList.vue 的搜索功能仅做本地过滤,数据受限于初始加载的1000条(pageSize: 1000),
输入搜索关键词时不会向后端发起新的请求,导致不在前1000条内的项目无法被搜到。

修复策略:
1. medicineList.vue:当用户输入搜索关键词时,新增 searchList() 走服务端搜索(调用 API 的 searchKey 参数),
   使用 pageSize: 5000 提高搜索覆盖率;搜索词清空时恢复使用预加载数据
2. 放宽 childrenJson 过滤条件,从严格的 == null 改为兼容空字符串(== null || === '')
3. 修复 diagnosisTreatmentDialog.vue 中 medicineList 的重复 import
2026-05-13 17:17:02 +08:00
关羽
31f7c4f32a Fix Bug #455: 门诊医生站-医嘱:开立诊疗医嘱时执行科室默认获取逻辑有误且显示为原始ID
根因分析:
- setValue函数对所有类型统一设置orgId=row.positionId
- 诊疗项目(adviceType=3)的positionId来自adm_organization_location配置表
- 当配置ID不在当前机构树中时,findOrgNameById返回空,el-tree-select显示原始ID
- 后续if(!orgId)判断因已有值而不触发回退到患者就诊科室

修复方案:
- 诊疗类型(adviceType=3)跳过positionId赋值,使用患者就诊科室作为默认执行科室
- 同时修复syncGroupFields中会覆盖orgId的问题

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 16:13:35 +08:00
关羽
480663f716 Fix Bug #454: 门诊医生站-医嘱页签:删除"待签发"状态的检验项目时,错误触发"执行科室"校验导致删除失败
在handService的执行科室校验中增加dbOpType DELETE跳过条件,作为BugFix#454过滤器的第二层防御。
同时修复handMedication和handService中重复声明requestId变量导致的编译错误。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 16:12:38 +08:00
关羽
cc484d5f10 Fix Bug #442: 手术计费:点击"删除"待签发耗材时异常报错,导致操作失败
根因:手术计费场景中"待签发"耗材的 requestId 来自 adm_charge_item.service_id,
当 service_id 为 null 或对应的 wor_device_request 记录不存在时,
后端 removeById(null) 或 removeById(不存在ID) 会抛出异常导致删除失败。

修复策略:
- 前端(prescriptionlist.vue): handleDelete 中增加 requestId 有效性校验,
  过滤掉 requestId 为 null/undefined/空的项,避免发送无效删除请求
- 后端(DoctorStationAdviceAppServiceImpl.java): handMedication/handDevice/handService
  三个删除路径增加 requestId null check,跳过无效记录而非抛出异常

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 15:16:45 +08:00
赵云
30f8cdbd80 Fix Bug #428: 门诊医生站-检查申请:未实现分类联动检查方法及套餐明细展示与勾选逻辑
修复三个根因问题:
1. handleCollapseChange 从 filteredCategoryList(计算属性映射副本)查找分类,
   改为从 categoryList(原始响应式数组)查找,确保 handleCategoryExpand 对
   cat.methods 的赋值能正确触发 Vue 响应式更新,分类展开后检查方法列表正常渲染
2. handleMethodSelect 跨分类检查中 cat.typeCode 与 newItem.checkType(cat.typeName)
   类型不匹配,改为统一使用 cat.typeName 比较
3. handleItemSelect 同样存在 typeCode vs typeName 不匹配问题,一并修复

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 15:12:09 +08:00
赵云
f4c6c12ef8 Fix Bug #441: 门诊手术安排:手术室护士角色进入页面提示"无权限"且"获取卫生机构列表失败"
根因:HTTP 响应拦截器(request.js)会对所有非 200 响应自动弹出错误通知。
手术室护士角色调用 /system/tenant/page 接口返回 403 时,拦截器先于组件的
.catch() 处理程序弹出"当前操作没有权限"通知,阻断页面体验。

修复:新增 getTenantPageSilent 包装函数,通过 skipErrorMsg: true 配置跳过
拦截器的自动错误提示,让 .catch() 中的 console.warn 静默降级处理。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-13 15:09:12 +08:00
关羽
8cf98008ae Fix Bug #426: 门诊医生站-检查开立:已选择列表应支持树形展开,显示套餐明细(项目/数量/单价)
根因:CheckPart 实体只有 packageName 字段,没有 packageId 字段。
前端 loadCategoryList 中 packageId 永远为 null,导致 loadPackageDetailsForItem
的 guard 条件 (!item.packageId) 永远提前返回,套餐明细无法加载。

修复策略:
1. handleItemSelect 中添加 packageName 到 selectedItems 数据对象
2. loadPackageDetailsForItem 改为优先使用 packageId,若无则通过 packageName
   调用 listCheckPackage API 查找 packageId(复用 loadMethodPackageDetails 已有的模式)
2026-05-13 15:05:51 +08:00
22 changed files with 200 additions and 266 deletions

View File

@@ -106,4 +106,9 @@ public class OpScheduleDto extends OpSchedule {
* 创建人名称 * 创建人名称
*/ */
private String createByName; private String createByName;
/**
* 费用类别
*/
private String feeType;
} }

View File

@@ -228,8 +228,10 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// 医嘱定义ID集合 // 医嘱定义ID集合
List<Long> adviceDefinitionIdList = adviceBaseDtoList.stream().map(AdviceBaseDto::getAdviceDefinitionId) List<Long> adviceDefinitionIdList = adviceBaseDtoList.stream().map(AdviceBaseDto::getAdviceDefinitionId)
.collect(Collectors.toList()); .collect(Collectors.toList());
// 费用定价主表ID集合 // 费用定价主表ID集合过滤null值手术项目无定价定义
List<Long> chargeItemDefinitionIdList = adviceBaseDtoList.stream().map(AdviceBaseDto::getChargeItemDefinitionId) List<Long> chargeItemDefinitionIdList = adviceBaseDtoList.stream()
.map(AdviceBaseDto::getChargeItemDefinitionId)
.filter(Objects::nonNull)
.collect(Collectors.toList()); .collect(Collectors.toList());
// 判断是否包含药品或耗材类型(只有这些类型才需要库存相关查询) // 判断是否包含药品或耗材类型(只有这些类型才需要库存相关查询)
@@ -275,9 +277,9 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
medLocationConfig = Collections.emptyList(); medLocationConfig = Collections.emptyList();
allowedLocByCategory = Collections.emptyMap(); allowedLocByCategory = Collections.emptyMap();
} }
// 费用定价子表信息 - 使用分批处理避免大量参数问题 // 费用定价子表信息 - 仅药品/耗材需要批次定价查询,手术/诊疗无库存概念不需要
List<AdvicePriceDto> childCharge = new ArrayList<>(); List<AdvicePriceDto> childCharge = new ArrayList<>();
if (chargeItemDefinitionIdList != null && !chargeItemDefinitionIdList.isEmpty()) { if (hasMedOrDevice && chargeItemDefinitionIdList != null && !chargeItemDefinitionIdList.isEmpty()) {
// 分批处理每批最多1000个ID增加批次大小以减少查询次数 // 分批处理每批最多1000个ID增加批次大小以减少查询次数
int batchSize = 1000; int batchSize = 1000;
for (int i = 0; i < chargeItemDefinitionIdList.size(); i += batchSize) { for (int i = 0; i < chargeItemDefinitionIdList.size(); i += batchSize) {

View File

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

View File

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

View File

@@ -99,7 +99,7 @@ public class RequestFormManageController {
* @param startDate 开始日期可选格式yyyy-MM-dd * @param startDate 开始日期可选格式yyyy-MM-dd
* @param endDate 结束日期可选格式yyyy-MM-dd * @param endDate 结束日期可选格式yyyy-MM-dd
* @param status 单据状态(可选) * @param status 单据状态(可选)
* @param keyword 关键字(可选,申请单号/检验项目名称模糊匹配) * @param keyword 关键字(可选,申请单号/检验项目模糊匹配)
* @return 检验申请单 * @return 检验申请单
*/ */
@GetMapping(value = "/get-inspection") @GetMapping(value = "/get-inspection")
@@ -163,7 +163,7 @@ public class RequestFormManageController {
@RequestParam(required = false) Long applyDeptId, @RequestParam(required = false) Long applyDeptId,
@RequestParam(defaultValue = "1") Integer pageNo, @RequestParam(defaultValue = "1") Integer pageNo,
@RequestParam(defaultValue = "10") Integer pageSize) { @RequestParam(defaultValue = "10") Integer pageSize) {
RequestFormDto dto = new RequestFormDto(surgeryNo, null, applyTimeStart, applyTimeEnd, RequestFormDto dto = new RequestFormDto(surgeryNo, ActivityDefCategory.PROCEDURE.getCode(), applyTimeStart, applyTimeEnd,
mainDoctorId, applyDeptId, pageNo, pageSize); mainDoctorId, applyDeptId, pageNo, pageSize);
return R.ok(iRequestFormManageAppService.getRequestFormPage(dto)); return R.ok(iRequestFormManageAppService.getRequestFormPage(dto));
} }

View File

@@ -89,12 +89,15 @@
cs.apply_doctor_name AS apply_doctor_name, cs.apply_doctor_name AS apply_doctor_name,
drf.create_time AS apply_time, drf.create_time AS apply_time,
os.surgery_nature AS surgeryType, os.surgery_nature AS surgeryType,
os.fee_type AS feeType, fc.contract_name AS feeType,
COALESCE(pi.identifier_no, ap.bus_no, '') AS identifierNo COALESCE(pi.identifier_no, ap.bus_no, '') AS identifierNo
FROM op_schedule os FROM op_schedule os
LEFT JOIN adm_patient ap ON os.patient_id = ap.id LEFT JOIN adm_patient ap ON os.patient_id = ap.id
INNER JOIN cli_surgery cs ON os.oper_code = cs.surgery_no AND cs.delete_flag = '0' INNER JOIN cli_surgery cs ON os.oper_code = cs.surgery_no AND cs.delete_flag = '0'
LEFT JOIN adm_organization o ON cs.org_id = o.id LEFT JOIN adm_organization o ON cs.org_id = o.id
LEFT JOIN adm_encounter ae ON ae.id = cs.encounter_id AND ae.delete_flag = '0'
LEFT JOIN adm_account aa ON aa.encounter_id = ae.id AND aa.delete_flag = '0'
LEFT JOIN fin_contract fc ON fc.bus_no = aa.contract_no AND fc.delete_flag = '0'
LEFT JOIN doc_request_form drf ON drf.prescription_no=cs.surgery_no LEFT JOIN doc_request_form drf ON drf.prescription_no=cs.surgery_no
LEFT JOIN ( LEFT JOIN (
SELECT patient_id, identifier_no SELECT patient_id, identifier_no

View File

@@ -280,17 +280,9 @@
aa.balance_amount aa.balance_amount
) AS personal_account ) AS personal_account
ON personal_account.encounter_id = ae.id ON personal_account.encounter_id = ae.id
LEFT JOIN ( LEFT JOIN med_medication_dispense mmd
SELECT med_req_id, status_enum
FROM (
SELECT med_req_id, status_enum,
ROW_NUMBER() OVER (PARTITION BY med_req_id ORDER BY id DESC) AS rn
FROM med_medication_dispense
WHERE delete_flag = '0'
) t
WHERE rn = 1
) mmd
ON mmd.med_req_id = T1.id ON mmd.med_req_id = T1.id
AND mmd.delete_flag = '0'
WHERE T1.delete_flag = '0' WHERE T1.delete_flag = '0'
AND T1.refund_medicine_id IS NULL AND T1.refund_medicine_id IS NULL
AND T1.generate_source_enum = #{doctorPrescription} AND T1.generate_source_enum = #{doctorPrescription}

View File

@@ -288,7 +288,7 @@
AND T1.refund_device_id IS NULL AND T1.refund_device_id IS NULL
ORDER BY T1.status_enum) ORDER BY T1.status_enum)
UNION ALL UNION ALL
(SELECT CASE WHEN T1.category_enum = 4 THEN 6 ELSE COALESCE(T1.category_enum, 3) END AS advice_type, (SELECT CASE WHEN T1.category_enum = 4 THEN 6 ELSE 3 END AS advice_type,
T1.id AS request_id, T1.id AS request_id,
T1.id || '-3' AS unique_key, T1.id || '-3' AS unique_key,
T1.requester_id AS requester_id, T1.requester_id AS requester_id,
@@ -373,4 +373,4 @@
</if> </if>
</select> </select>
</mapper> </mapper>

View File

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

View File

@@ -193,9 +193,6 @@ public class OpSchedule extends HisBaseEntity {
/** 外请专家姓名 */ /** 外请专家姓名 */
private String externalExpertName; private String externalExpertName;
/** 费用类别 */
private String feeType;
/** 备注信息 */ /** 备注信息 */
private String remark; private String remark;

View File

@@ -15,7 +15,7 @@
DELETE DELETE
FROM adm_encounter_diagnosis FROM adm_encounter_diagnosis
WHERE encounter_id = #{encounterId} WHERE encounter_id = #{encounterId}
AND tcm_flag = 1 AND tcm_flag = 0
</delete> </delete>
<select id="getEncounterDiagnosisByEncounterConDefId" <select id="getEncounterDiagnosisByEncounterConDefId"
resultType="com.openhis.administration.domain.EncounterDiagnosis"> resultType="com.openhis.administration.domain.EncounterDiagnosis">

View File

@@ -226,18 +226,8 @@ function getList() {
getDiagnosisTreatmentList(queryParams.value).then((res) => { getDiagnosisTreatmentList(queryParams.value).then((res) => {
loading.value = false; loading.value = false;
catagoryList.value = res.data.records.map(record => { catagoryList.value = res.data.records.map(record => {
// 为每一行初始化 filteredOptions确保显示框能正确显示项目名称
const filteredOptions = allImplementDepartmentList.value.slice(0, 100); const filteredOptions = allImplementDepartmentList.value.slice(0, 100);
// 如果后端返回了_dictText名称但选项列表中不存在该ID如不在前100条
// 手动添加以确保 el-select 能正确显示项目名称而非ID码
if (record.activityDefinitionId_dictText && record.activityDefinitionId) {
const exists = filteredOptions.some(o => o.value === String(record.activityDefinitionId));
if (!exists) {
filteredOptions.push({
value: String(record.activityDefinitionId),
label: record.activityDefinitionId_dictText,
});
}
}
return { return {
...record, ...record,
loading: false, loading: false,

View File

@@ -466,9 +466,9 @@ function calculateTotalPrice() {
try { try {
let sum = 0; let sum = 0;
treatmentItems.value.forEach((item) => { treatmentItems.value.forEach((item) => {
if (item.adviceDefinitionId && item.retailPrice && item.childrenRequestNum) { if (item.adviceDefinitionId && item.adviceDefinitionId !== '') {
const price = parseFloat(item.retailPrice) || 0; const price = Number(item.retailPrice) || 0;
const count = parseInt(item.childrenRequestNum) || 0; const count = Number(item.childrenRequestNum) || 0;
sum += price * count; sum += price * count;
} }
}); });
@@ -478,7 +478,10 @@ function calculateTotalPrice() {
(item) => item.adviceDefinitionId && item.adviceDefinitionId !== '' (item) => item.adviceDefinitionId && item.adviceDefinitionId !== ''
); );
if (hasValidItem) { if (hasValidItem) {
form.value.retailPrice = parseFloat(totalPrice.value); // 使用 nextTick 确保总价更新后零售价才更新,避免 Vue 响应式时序问题
nextTick(() => {
form.value.retailPrice = parseFloat(totalPrice.value) || 0;
});
} else { } else {
form.value.retailPrice = undefined; form.value.retailPrice = undefined;
} }
@@ -564,15 +567,16 @@ function edit() {
form.value.pricingFlag = 1; form.value.pricingFlag = 1;
} }
// 处理子项数据确保包含retailPrice字段 // 处理子项数据确保包含retailPrice和name字段
if (props.item.childrenJson) { if (props.item.childrenJson) {
const parsedItems = JSON.parse(props.item.childrenJson); const parsedItems = JSON.parse(props.item.childrenJson);
treatmentItems.value = parsedItems.map((item) => ({ treatmentItems.value = parsedItems.map((item) => ({
...item, ...item,
name: item.name || '',
retailPrice: item.retailPrice || 0, retailPrice: item.retailPrice || 0,
})); }));
} else { } else {
treatmentItems.value = [{ adviceDefinitionId: '', childrenRequestNum: 1, retailPrice: 0 }]; treatmentItems.value = [{ adviceDefinitionId: '', childrenRequestNum: 1, name: '', retailPrice: 0 }];
} }
form.value.permittedUnitCode = form.value.permittedUnitCode form.value.permittedUnitCode = form.value.permittedUnitCode
? form.value.permittedUnitCode.toString() ? form.value.permittedUnitCode.toString()
@@ -617,7 +621,7 @@ function reset() {
chrgitmLv: undefined, //医保等级 chrgitmLv: undefined, //医保等级
pricingFlag: 1, // 划价标记,默认允许划价 pricingFlag: 1, // 划价标记,默认允许划价
}; };
treatmentItems.value = [{ adviceDefinitionId: '', childrenRequestNum: 1, retailPrice: 0 }]; treatmentItems.value = [{ adviceDefinitionId: '', childrenRequestNum: 1, name: '', retailPrice: 0 }];
totalPrice.value = '0.00'; totalPrice.value = '0.00';
proxy.resetForm('diagnosisTreatmentRef'); proxy.resetForm('diagnosisTreatmentRef');
} }
@@ -759,7 +763,10 @@ function selectRow(row, index) {
treatmentItems.value[index].adviceDefinitionId = row.id; treatmentItems.value[index].adviceDefinitionId = row.id;
treatmentItems.value[index].retailPrice = row.retailPrice || 0; treatmentItems.value[index].retailPrice = row.retailPrice || 0;
medicineSearchKey.value = ''; medicineSearchKey.value = '';
calculateTotalPrice(); // 使用 nextTick 确保 DOM 更新后再计算总价
nextTick(() => {
calculateTotalPrice();
});
} }
// 清空诊疗子项 // 清空诊疗子项

View File

@@ -711,6 +711,7 @@ async function handleCategoryExpand(cat) {
code: m.code, code: m.code,
price: m.price || 0, price: m.price || 0,
packageName: m.packageName || '', packageName: m.packageName || '',
packageId: m.packageId || null,
packagePrice: m.packagePrice || null, packagePrice: m.packagePrice || null,
serviceFee: m.serviceFee || null serviceFee: m.serviceFee || null
})); }));
@@ -1024,11 +1025,16 @@ function handleRowClick(row) {
activeDetailTab.value = 'applyForm'; activeDetailTab.value = 'applyForm';
request({ url: `/exam/apply/${row.applyNo}`, method: 'get' }).then(async res => { request({ url: `/exam/apply/${row.applyNo}`, method: 'get' }).then(async res => {
const resp = res.data || res; const resp = res.data || res;
// 保存 items 在顶层响应中,避免后面 d.data 赋值后丢失 // Bug #408修复: items 在 AjaxResult 顶层(res.items / resp.items),不在 ExamApply 对象内
const rawItems = resp.items; // 防御性提取:优先取顶层 items兼容嵌套在 resp.data.items 的情况
let rawItems = res.items || resp.items;
if (!rawItems && resp.data && typeof resp.data === 'object') {
rawItems = resp.data.items;
}
rawItems = rawItems || [];
const d = resp.data || resp; const d = resp.data || resp;
if (d) Object.assign(form, d); if (d) Object.assign(form, d);
if (rawItems && Array.isArray(rawItems)) { if (Array.isArray(rawItems) && rawItems.length > 0) {
try { try {
// 为每个项目加载检查方法 // 为每个项目加载检查方法
const itemsWithMethods = await Promise.all(rawItems.map(async m => { const itemsWithMethods = await Promise.all(rawItems.map(async m => {
@@ -1059,12 +1065,18 @@ function handleRowClick(row) {
code: md.code, code: md.code,
price: m.itemFee || 0, // fallback 到已保存的价格 price: m.itemFee || 0, // fallback 到已保存的价格
packageName: md.packageName || '', packageName: md.packageName || '',
packageId: md.packageId || null,
packagePrice: md.packagePrice || null, // Bug #384修复: 套餐价格 packagePrice: md.packagePrice || null, // Bug #384修复: 套餐价格
serviceFee: md.serviceFee || null serviceFee: md.serviceFee || null
})); }));
// 如果有已保存的检查方法信息,尝试匹配 // 如果有已保存的检查方法信息,尝试匹配
if (m.checkMethodId) { if (m.checkMethodId) {
item.selectedMethod = item.methods.find(md => md.id === m.checkMethodId) || null; item.selectedMethod = item.methods.find(md => md.id === m.checkMethodId) || null;
// 从已保存的方法中获取套餐信息
if (item.selectedMethod?.packageId) {
item.isPackage = true;
item.packageId = item.selectedMethod.packageId;
}
} }
} }
} catch (err) { } catch (err) {
@@ -1125,6 +1137,13 @@ async function handleMethodSelect(checked, method, cat) {
const existingItem = selectedItems.value.find(s => s.id === targetItem.id); const existingItem = selectedItems.value.find(s => s.id === targetItem.id);
if (existingItem) { if (existingItem) {
existingItem.selectedMethod = method; existingItem.selectedMethod = method;
// 从方法中获取套餐信息
if (method.packageId) {
existingItem.isPackage = true;
existingItem.packageId = method.packageId;
// 预加载套餐明细
loadPackageDetailsForItem(existingItem);
}
updateMethodDisplay(); updateMethodDisplay();
return; return;
} }
@@ -1140,7 +1159,7 @@ async function handleMethodSelect(checked, method, cat) {
} }
} }
selectedItems.value.push({ const newItem = {
id: targetItem.id, name: targetItem.name, id: targetItem.id, name: targetItem.name,
price: targetItem.price, quantity: 1, price: targetItem.price, quantity: 1,
serviceFee: targetItem.serviceFee || 0, serviceFee: targetItem.serviceFee || 0,
@@ -1152,9 +1171,16 @@ async function handleMethodSelect(checked, method, cat) {
methods: [method], methods: [method],
selectedMethod: method, selectedMethod: method,
expanded: false, expanded: false,
isPackage: !!targetItem.packageName, // 从方法中获取套餐信息(优先级高于项目本身的 packageName
packageId: targetItem.packageId || null isPackage: !!method.packageId || !!targetItem.packageName,
}); packageId: method.packageId || targetItem.packageId || null
};
selectedItems.value.push(newItem);
// 如果是套餐,预加载套餐明细
if (newItem.isPackage && newItem.packageId) {
loadPackageDetailsForItem(newItem);
}
// 自动回填执行科室 // 自动回填执行科室
if (selectedItems.value.length === 1 && cat?.performDeptName) { if (selectedItems.value.length === 1 && cat?.performDeptName) {
@@ -1199,6 +1225,7 @@ async function handleItemSelect(checked, item, cat) {
code: m.code, code: m.code,
price: m.price || item.price, // fallback 到项目价格 price: m.price || item.price, // fallback 到项目价格
packageName: m.packageName || '', packageName: m.packageName || '',
packageId: m.packageId || null,
packagePrice: m.packagePrice || null, // Bug #384修复: 套餐价格 packagePrice: m.packagePrice || null, // Bug #384修复: 套餐价格
serviceFee: m.serviceFee || null // Bug #384修复: 服务费 serviceFee: m.serviceFee || null // Bug #384修复: 服务费
})); }));

View File

@@ -169,7 +169,6 @@
<script setup> <script setup>
import {getCurrentInstance} from 'vue'; // 添加 nextTick 导入 import {getCurrentInstance} from 'vue'; // 添加 nextTick 导入
import useUserStore from '@/store/modules/user'; import useUserStore from '@/store/modules/user';
import { formatDateStr } from '@/utils';
import { import {
delEncounterDiagnosis, delEncounterDiagnosis,
deleteDiagnosisBind, deleteDiagnosisBind,
@@ -287,22 +286,10 @@ function getList() {
if (obj.diagSrtNo == null) { if (obj.diagSrtNo == null) {
obj.diagSrtNo = '1'; obj.diagSrtNo = '1';
} }
// 补充缺失的元数据字段
if (!obj.diagnosisDoctor) {
obj.diagnosisDoctor = props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name;
}
if (!obj.diagnosisTime) {
obj.diagnosisTime = item.diagnosisTime || getCurrentDate();
}
if (!obj.classification) {
obj.classification = item.typeName === '中医诊断' ? '中医' : '西医';
}
if (obj.longTermFlag == null) {
obj.longTermFlag = 0;
}
return obj; return obj;
}); });
form.value.diagnosisList = datas; form.value.diagnosisList = datas;
// form.value.diagnosisList = res.data;
emits('diagnosisSave', false); emits('diagnosisSave', false);
} }
}); });
@@ -318,16 +305,13 @@ function getList() {
ybNo: item.ybNo, ybNo: item.ybNo,
medTypeCode: item.medTypeCode, medTypeCode: item.medTypeCode,
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name, diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: item.diagnosisTime || getCurrentDate(), diagnosisTime: new Date().toLocaleString('zh-CN')
diagSrtNo: item.diagSrtNo,
classification: '中医',
longTermFlag: item.longTermFlag || 0
}); });
}); });
// 将新数据添加到现有列表中 // 将新数据添加到现有列表中
form.value.diagnosisList.push(...newList); form.value.diagnosisList.push(...newList);
// 重新排序整个列表 // 重新排序整个列表
form.value.diagnosisList.sort((a, b) => { form.value.diagnosisList.sort((a, b) => {
const aNo = typeof a.diagSrtNo === 'number' ? a.diagSrtNo : 9999; const aNo = typeof a.diagSrtNo === 'number' ? a.diagSrtNo : 9999;
@@ -338,7 +322,7 @@ function getList() {
emits('diagnosisSave', false); emits('diagnosisSave', false);
} }
}); });
getTree(); getTree();
} }
@@ -355,12 +339,11 @@ function handleImport() {
if (!props.patientInfo || !props.patientInfo.encounterId) { if (!props.patientInfo || !props.patientInfo.encounterId) {
return; return;
} }
if (props.patientInfo.contractName != '自费') { if (props.patientInfo.contractName != '自费') {
// 获取患者慢性病信息 // 获取患者慢性病信息
getChronicDisease({ encounterId: props.patientInfo.encounterId }).then((res) => { getChronicDisease({ encounterId: props.patientInfo.encounterId }).then((res) => {
if (res.data && res.data.length > 0) { if (res.data && res.data.length > 0) {
const currentDate = getCurrentDate();
res.data.forEach((item, index) => { res.data.forEach((item, index) => {
form.value.diagnosisList.push({ form.value.diagnosisList.push({
...item, ...item,
@@ -372,10 +355,7 @@ function handleImport() {
iptDiseTypeCode: 2, iptDiseTypeCode: 2,
diagnosisDesc: '', diagnosisDesc: '',
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name, diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: currentDate, diagnosisTime: new Date().toLocaleString('zh-CN'),
onsetDate: currentDate,
classification: '西医',
longTermFlag: 0,
//添加 patientId //添加 patientId
patientId: props.patientInfo.patientId patientId: props.patientInfo.patientId
}, },
@@ -490,7 +470,6 @@ function handleAddDiagnosis() {
* 添加诊断项 * 添加诊断项
*/ */
function addDiagnosisItem() { function addDiagnosisItem() {
const currentDate = getCurrentDate();
form.value.diagnosisList.push({ form.value.diagnosisList.push({
showPopover: false, showPopover: false,
name: undefined, name: undefined,
@@ -500,10 +479,9 @@ function addDiagnosisItem() {
iptDiseTypeCode: 2, iptDiseTypeCode: 2,
diagnosisDesc: '', diagnosisDesc: '',
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name, diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: currentDate, diagnosisTime: new Date().toLocaleString('zh-CN'),
classification: '西医',
onsetDate: currentDate, // 新增这一行:为每个诊断项添加 patientId
longTermFlag: 0,
patientId: props.patientInfo.patientId patientId: props.patientInfo.patientId
}); });
@@ -580,7 +558,16 @@ function handleMaindise(value, index) {
/** /**
* 保存诊断 * 保存诊断
*/ */
async function handleSaveDiagnosis() { /**
* 保存诊断
*/
/**
* 保存诊断
*/
/**
* 保存诊断
*/
function handleSaveDiagnosis() {
for (let index = 0; index < (form.value.diagnosisList || []).length; index++) { for (let index = 0; index < (form.value.diagnosisList || []).length; index++) {
const item = form.value.diagnosisList[index]; const item = form.value.diagnosisList[index];
if (!item.diagSrtNo) { if (!item.diagSrtNo) {
@@ -591,7 +578,7 @@ async function handleSaveDiagnosis() {
break; break;
} }
} }
proxy.$refs.formRef.validate(async (valid) => { proxy.$refs.formRef.validate((valid) => {
if (valid) { if (valid) {
if (form.value.diagnosisList.length === 0) { if (form.value.diagnosisList.length === 0) {
proxy.$modal.msgWarning('诊断不能为空'); proxy.$modal.msgWarning('诊断不能为空');
@@ -604,51 +591,43 @@ async function handleSaveDiagnosis() {
// 设置保存标志避免触发watch监听器 // 设置保存标志避免触发watch监听器
isSaving.value = true; isSaving.value = true;
try { // 步骤1深拷贝并按 diagSrtNo 排序
// 步骤1深拷贝并按 diagSrtNo 排序 const sortedList = [...form.value.diagnosisList].sort((a, b) => {
const sortedList = [...form.value.diagnosisList].sort((a, b) => { const aNo = typeof a.diagSrtNo === 'number' ? a.diagSrtNo : 9999;
const aNo = typeof a.diagSrtNo === 'number' ? a.diagSrtNo : 9999; const bNo = typeof b.diagSrtNo === 'number' ? b.diagSrtNo : 9999;
const bNo = typeof b.diagSrtNo === 'number' ? b.diagSrtNo : 9999; return aNo - bNo;
return aNo - bNo; });
});
// 步骤2转换日期格式为后端期望的格式 // 步骤2重新分配连续的序号从1开始
const diagnosisChildList = sortedList.map(item => ({ sortedList.forEach((item, index) => {
...item, item.diagSrtNo = index + 1; // 这里是关键!把“诊断排序”改成新顺序
onsetDate: item.onsetDate ? formatDateStr(item.onsetDate, 'YYYY/M/D HH:mm:ss') : null, });
diagnosisTime: item.diagnosisTime ? formatDateStr(item.diagnosisTime, 'YYYY/M/D HH:mm:ss') : null
}));
// 步骤3提交排序后的数据
const res = await saveDiagnosis({
patientId: props.patientInfo.patientId,
encounterId: props.patientInfo.encounterId,
diagnosisChildList: diagnosisChildList,
});
// 步骤3提交排序后的数据
saveDiagnosis({
patientId: props.patientInfo.patientId,
encounterId: props.patientInfo.encounterId,
diagnosisChildList: sortedList,
}).then((res) => {
if (res.code === 200) { if (res.code === 200) {
// 步骤4从服务器刷新数据,确保获取最新的诊断信息(避免重复记录) // 步骤4更新本地数据,使用全新对象防止响应式问题
await getList(); form.value.diagnosisList = sortedList.map(item => ({ ...item }));
getTree();
emits('diagnosisSave', false); emits('diagnosisSave', false);
proxy.$modal.msgSuccess('诊断已保存'); proxy.$modal.msgSuccess('诊断已保存');
// 食源性疾病逻辑 // 食源性疾病逻辑
isFoodDiseasesNew({ encounterId: props.patientInfo.encounterId }).then((res2) => { isFoodDiseasesNew({ encounterId: props.patientInfo.encounterId }).then((res2) => {
if (res2.code === 200 && res2.data) { if (res2.code === 20 && res2.data) {
window.open(res2.data, '_blank'); window.open(res2.data, '_blank');
} }
}); });
} }
} catch (error) { }).finally(() => {
console.error('保存诊断失败:', error);
const errorMsg = error?.response?.data?.msg || error?.message || '保存诊断失败,请稍后重试';
proxy.$modal.msgError(errorMsg);
} finally {
setTimeout(() => { setTimeout(() => {
isSaving.value = false; isSaving.value = false;
}, 100); }, 100);
} });
} }
}); });
} }
@@ -708,7 +687,6 @@ function handleNodeClick(data) {
proxy.$modal.msgWarning('该诊断项已存在'); proxy.$modal.msgWarning('该诊断项已存在');
return; return;
} }
const currentDate = getCurrentDate();
form.value.diagnosisList.push({ form.value.diagnosisList.push({
ybNo: data.ybNo, ybNo: data.ybNo,
name: data.name, name: data.name,
@@ -716,12 +694,9 @@ function handleNodeClick(data) {
medTypeCode: undefined, medTypeCode: undefined,
diagSrtNo: form.value.diagnosisList.length + 1, diagSrtNo: form.value.diagnosisList.length + 1,
definitionId: data.definitionId, definitionId: data.definitionId,
classification: '西医',
onsetDate: currentDate,
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name, diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: currentDate, diagnosisTime: new Date().toLocaleString('zh-CN'),
longTermFlag: 0, // 添加 patientId
selectedDiseases: data.ybNo ? [data.ybNo] : [],
patientId: props.patientInfo.patientId patientId: props.patientInfo.patientId
}); });
if (form.value.diagnosisList.length == 1) { if (form.value.diagnosisList.length == 1) {

View File

@@ -206,7 +206,7 @@ onMounted(() => {
* type(1watch监听类型 2:点击保存类型) * type(1watch监听类型 2:点击保存类型)
* selectProjectIds(选中项目的id数组) * selectProjectIds(选中项目的id数组)
* */ * */
const projectWithDepartment = (selectProjectIds) => { const projectWithDepartment = (selectProjectIds, type) => {
//1.获取选中的项目 2.判断项目的执行科室是否相同 3.判断执行科室是否配置 4.将项目的执行科室复值到执行科室下拉选位置 //1.获取选中的项目 2.判断项目的执行科室是否相同 3.判断执行科室是否配置 4.将项目的执行科室复值到执行科室下拉选位置
let isRelease = true; let isRelease = true;
// 选中项目的数组 // 选中项目的数组

View File

@@ -483,7 +483,7 @@ const submit = () => {
encounterId: patientInfo.value.encounterId, encounterId: patientInfo.value.encounterId,
organizationId: patientInfo.value.inHospitalOrgId, organizationId: patientInfo.value.inHospitalOrgId,
requestFormId: '', requestFormId: '',
name: '检查申请单', name: applicationListAllFilter.map(item => item.adviceName).join('、'),
descJson: JSON.stringify(submitForm), descJson: JSON.stringify(submitForm),
categoryEnum: '2', categoryEnum: '2',
}).then((res) => { }).then((res) => {

View File

@@ -5,16 +5,14 @@
--> -->
<template> <template>
<div class="surgery-container"> <div class="surgery-container">
<div class="transfer-wrapper"> <div v-loading="loading" class="transfer-wrapper" style="min-height: 300px;">
<div v-loading="loading" style="min-height: 300px;"> <el-transfer
<el-transfer v-model="transferValue"
v-model="transferValue" :data="applicationList"
:data="applicationList" filter-placeholder="项目代码/名称"
filter-placeholder="项目代码/名称" filterable
filterable :titles="['未选择', '已选择']"
:titles="['未选择', '已选择']" />
/>
</div>
</div> </div>
<div class="bloodTransfusion-form"> <div class="bloodTransfusion-form">
<el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="demo-ruleForm"> <el-form :model="form" :rules="rules" ref="formRef" label-width="120px" class="demo-ruleForm">
@@ -103,8 +101,8 @@ const findTreeItem = (list, id) => {
const emits = defineEmits(['submitOk']); const emits = defineEmits(['submitOk']);
const props = defineProps({}); const props = defineProps({});
const state = reactive({}); const state = reactive({});
const applicationListAll = ref([]); const applicationListAll = ref();
const applicationList = ref([]); const applicationList = ref();
const orgOptions = ref([]); // 科室选项 const orgOptions = ref([]); // 科室选项
const loading = ref(false); // 加载状态 const loading = ref(false); // 加载状态
const getList = () => { const getList = () => {

View File

@@ -631,12 +631,6 @@ function getListInfo(addNewRow) {
handleAddPrescription(); handleAddPrescription();
} }
}); });
}).catch((err) => {
console.error('处方列表加载失败:', err);
// 🔧 Bug #405 修复列表加载失败时确保所有行被锁定isEdit=false
// 防止行永久处于可编辑状态
prescriptionList.value.forEach(item => { item.isEdit = false; });
loadingInstance.close();
}); });
getContract({ encounterId: patientInfo.value.encounterId }).then((res) => { getContract({ encounterId: patientInfo.value.encounterId }).then((res) => {
contractList.value = res.data; contractList.value = res.data;
@@ -1187,19 +1181,27 @@ function handleSave() {
}); });
// 此处签发处方和单行保存处方传参相同后台已经将传参存为JSON字符串此处直接转换为JSON即可 // 此处签发处方和单行保存处方传参相同后台已经将传参存为JSON字符串此处直接转换为JSON即可
loading.value = true; loading.value = true;
let list = saveList.map((item) => { let list = [];
const parsedContent = JSON.parse(item.contentJson); try {
return { list = saveList.map((item) => {
...parsedContent, const parsedContent = item.contentJson ? JSON.parse(item.contentJson) || {} : {};
adviceType: item.adviceType, return {
requestId: item.requestId, ...parsedContent,
dbOpType: '1', adviceType: item.adviceType,
groupId: item.groupId, requestId: item.requestId,
uniqueKey: undefined, dbOpType: '1',
// 确保 therapyEnum 被正确传递 groupId: item.groupId,
therapyEnum: parsedContent.therapyEnum || item.therapyEnum || '1', uniqueKey: undefined,
}; // 确保 therapyEnum 被正确传递
}); therapyEnum: parsedContent.therapyEnum || item.therapyEnum || '1',
};
});
} catch (error) {
loading.value = false;
isSaving.value = false;
proxy.$modal.msgError('医嘱内容解析失败,请检查待签发医嘱');
return;
}
// 保存签发按钮 // 保存签发按钮
isSaving.value = true; isSaving.value = true;
console.log('签发处方参数:', { console.log('签发处方参数:', {
@@ -1449,12 +1451,6 @@ function handleSaveBatch() {
}) })
.catch((error) => { .catch((error) => {
isSaving.value = false; isSaving.value = false;
// 🔧 Bug #405 修复:保存失败时也锁定行,防止行永久处于可编辑状态
filterPrescriptionList.value.forEach(item => {
if (item.statusEnum == 1 && !item.requestId) {
item.isEdit = false;
}
});
proxy.$modal.msgError(error?.msg || '保存失败,请重试'); proxy.$modal.msgError(error?.msg || '保存失败,请重试');
}); });
} }

View File

@@ -302,6 +302,29 @@ function getSelectRows() {
}); });
return list; return list;
} }
function getTableRef(index) {
return proxy.$refs['tableRef' + index]?.[0];
}
function selectAllRows() {
prescriptionList.value.forEach((item, index) => {
const tableRef = getTableRef(index);
if (!tableRef) {
return;
}
item.forEach((row) => {
tableRef.toggleRowSelection(row, true);
});
});
}
function clearSelection() {
prescriptionList.value.forEach((item, index) => {
getTableRef(index)?.clearSelection();
});
}
function handleRateChange(value, item, row) { function handleRateChange(value, item, row) {
// 拼接当前选中时间 // 拼接当前选中时间
if (value) { if (value) {
@@ -319,6 +342,8 @@ function handleRateChange(value, item, row) {
defineExpose({ defineExpose({
handleGetPrescription, handleGetPrescription,
handleMedicineSummary, handleMedicineSummary,
selectAllRows,
clearSelection,
}); });
</script> </script>

View File

@@ -69,7 +69,11 @@
</div> </div>
<div> <div>
<span class="descriptions-item-label">全选</span> <span class="descriptions-item-label">全选</span>
<el-switch v-model="chooseAll" @change="handelSwicthChange" /> <el-switch
v-model="chooseAll"
:disabled="isDetails != '1'"
@change="handelSwicthChange"
/>
<el-button class="ml20 mr20" type="primary" @click="handleExecute"> 汇总领药 </el-button> <el-button class="ml20 mr20" type="primary" @click="handleExecute"> 汇总领药 </el-button>
</div> </div>
</div> </div>
@@ -160,24 +164,31 @@ function handleClick(tabName) {
} }
function handleGetPrescription() { function handleGetPrescription() {
prescriptionRefs.value.handleGetPrescription(); chooseAll.value = false;
prescriptionRefs.value?.handleGetPrescription();
} }
function handelSwicthChange() { function handelSwicthChange(value) {
if (chooseAll.value) { if (!prescriptionRefs.value) {
proxy.$refs['prescriptionRefs'].selectAllRows(); chooseAll.value = false;
return;
}
if (value) {
prescriptionRefs.value.selectAllRows();
} else { } else {
proxy.$refs['prescriptionRefs'].clearSelection(); prescriptionRefs.value.clearSelection();
} }
} }
function handleRadioChange(value) { function handleRadioChange(value) {
chooseAll.value = false;
if (value == '1') { if (value == '1') {
handleGetPrescription(); handleGetPrescription();
} }
} }
function handleTherapyChange() { function handleTherapyChange() {
chooseAll.value = false;
handleGetPrescription(); handleGetPrescription();
} }
@@ -216,4 +227,4 @@ provide('handleGetPrescription', (value) => {
:deep(.el-tabs__header) { :deep(.el-tabs__header) {
margin: 0; margin: 0;
} }
</style> </style>

View File

@@ -1,10 +0,0 @@
-- Bug #435: 门诊手术安排编辑弹窗中"费用类别"字段数据未回显
-- 原因op_schedule 表缺少 fee_type 字段,导致手术安排创建时费用类别未被持久化
ALTER TABLE op_schedule ADD COLUMN IF NOT EXISTS fee_type VARCHAR(50);
COMMENT ON COLUMN op_schedule.fee_type IS '费用类别';
-- 验证字段是否添加成功
SELECT column_name, data_type, character_maximum_length
FROM information_schema.columns
WHERE table_name = 'op_schedule'
AND column_name = 'fee_type';