Compare commits
1 Commits
develop
...
bug475-fix
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80695d90f9 |
@@ -1539,22 +1539,11 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
deviceRequest.setRequesterId(adviceSaveDto.getPractitionerId()); // 开方医生
|
||||
deviceRequest.setOrgId(adviceSaveDto.getFounderOrgId());// 开方人科室
|
||||
deviceRequest.setReqAuthoredTime(curDate); // 请求开始时间
|
||||
// 发放耗材房(若前端未传locationId,优先沿用已有DeviceRequest的performLocation,否则使用登录用户科室)
|
||||
// 发放耗材房(若前端未传locationId,使用登录用户的科室作为默认值)
|
||||
Long locId = adviceSaveDto.getLocationId();
|
||||
if (locId == null) {
|
||||
// 尝试从已有DeviceRequest获取原始的performLocation
|
||||
if (adviceSaveDto.getRequestId() != null) {
|
||||
DeviceRequest existingDevice = iDeviceRequestService.getById(adviceSaveDto.getRequestId());
|
||||
if (existingDevice != null && existingDevice.getPerformLocation() != null) {
|
||||
locId = existingDevice.getPerformLocation();
|
||||
log.info("耗材locationId为空,使用已有DeviceRequest的performLocation: locationId={}", locId);
|
||||
}
|
||||
}
|
||||
// 如果已有记录也没有performLocation,则使用登录用户科室作为兜底
|
||||
if (locId == null) {
|
||||
locId = SecurityUtils.getLoginUser().getOrgId();
|
||||
log.info("耗材locationId为空且无已有记录,使用登录用户科室作为默认值: locationId={}", locId);
|
||||
}
|
||||
log.info("耗材locationId为空,使用登录用户科室作为默认值: locationId={}", locId);
|
||||
}
|
||||
deviceRequest.setPerformLocation(locId);
|
||||
deviceRequest.setEncounterId(adviceSaveDto.getEncounterId()); // 就诊id
|
||||
|
||||
@@ -580,11 +580,7 @@ public class DoctorStationDiagnosisAppServiceImpl implements IDoctorStationDiagn
|
||||
@Override
|
||||
public R<?> saveInfectiousDiseaseReport(InfectiousDiseaseReportDto infectiousDiseaseReportDto) {
|
||||
// 检查卡片编号唯一性(新增时检查,编辑时排除当前记录)
|
||||
String cardNo = infectiousDiseaseReportDto.getCardNo();
|
||||
if (cardNo == null || cardNo.trim().isEmpty()) {
|
||||
return R.fail("卡片编号不能为空");
|
||||
}
|
||||
cardNo = cardNo.trim();
|
||||
String cardNo = infectiousDiseaseReportDto.getCardNo().trim();
|
||||
LambdaQueryWrapper<InfectiousDiseaseReport> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(InfectiousDiseaseReport::getCardNo, cardNo);
|
||||
long count = iInfectiousDiseaseReportService.count(queryWrapper);
|
||||
|
||||
@@ -343,10 +343,6 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
||||
}
|
||||
|
||||
// 写入 div_log 审计日志(独立于队列项,确保每次完诊都生成记录)
|
||||
// 防重复:若队列项已是 COMPLETED 状态,说明护士站已处理过并写过分诊日志,不再重复写入
|
||||
boolean queueAlreadyCompleted = queueItem != null
|
||||
&& TriageQueueStatus.COMPLETED.getValue().equals(queueItem.getStatus());
|
||||
if (!queueAlreadyCompleted) {
|
||||
try {
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
DivLog divLog = new DivLog()
|
||||
@@ -361,7 +357,6 @@ public class DoctorStationMainAppServiceImpl implements IDoctorStationMainAppSer
|
||||
} catch (Exception e) {
|
||||
log.error("写入div_log审计日志失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 更新状态、完成时间以及初复诊标识
|
||||
Date now = new Date();
|
||||
|
||||
@@ -178,26 +178,15 @@ public class AdviceUtils {
|
||||
// 生命提示信息集合
|
||||
List<String> tipsList = new ArrayList<>();
|
||||
for (MedicationRequestUseExe medicationRequestUseExe : medUseExeList) {
|
||||
// 第一步:按 performLocation 匹配指定药房的库存
|
||||
// 聚合同一位置所有批次的库存总量
|
||||
List<AdviceInventoryDto> matchedInventories = adviceInventory.stream()
|
||||
.filter(inventoryDto -> medicationRequestUseExe.getMedicationId().equals(inventoryDto.getItemId())
|
||||
&& CommonConstants.TableName.MED_MEDICATION_DEFINITION.equals(inventoryDto.getItemTable())
|
||||
&& (medicationRequestUseExe.getPerformLocation() == null
|
||||
|| medicationRequestUseExe.getPerformLocation().equals(inventoryDto.getLocationId()))
|
||||
&& medicationRequestUseExe.getPerformLocation().equals(inventoryDto.getLocationId())
|
||||
// 如果选择了具体的批次号,校验库存时需要加上批次号的匹配条件
|
||||
&& (StringUtils.isEmpty(medicationRequestUseExe.getLotNumber())
|
||||
|| medicationRequestUseExe.getLotNumber().equals(inventoryDto.getLotNumber())))
|
||||
.collect(Collectors.toList());
|
||||
// 第二步:如果指定药房没有匹配到库存,则放宽条件查询所有药房的库存
|
||||
if (matchedInventories.isEmpty()) {
|
||||
matchedInventories = adviceInventory.stream()
|
||||
.filter(inventoryDto -> medicationRequestUseExe.getMedicationId().equals(inventoryDto.getItemId())
|
||||
&& CommonConstants.TableName.MED_MEDICATION_DEFINITION.equals(inventoryDto.getItemTable())
|
||||
// 如果选择了具体的批次号,校验库存时需要加上批次号的匹配条件
|
||||
&& (StringUtils.isEmpty(medicationRequestUseExe.getLotNumber())
|
||||
|| medicationRequestUseExe.getLotNumber().equals(inventoryDto.getLotNumber())))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
// 匹配到库存信息
|
||||
if (!matchedInventories.isEmpty()) {
|
||||
// 聚合所有批次的可用库存
|
||||
|
||||
@@ -341,7 +341,7 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
&& (DbOpType.INSERT.getCode().equals(e.getDbOpType()) || DbOpType.UPDATE.getCode().equals(e.getDbOpType())))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 防重复保存:对所有医嘱进行去重(包括 INSERT 和 UPDATE 混合场景),避免签发单条医嘱时产生重复记录
|
||||
// 防重复保存:对新增医嘱进行去重,避免签发单条长期医嘱时产生重复记录
|
||||
Set<String> longUniqueKeySet = new HashSet<>();
|
||||
List<RegAdviceSaveDto> longUniqueList = new ArrayList<>();
|
||||
for (RegAdviceSaveDto adviceSaveDto : longInsertOrUpdateList) {
|
||||
@@ -351,10 +351,10 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
+ adviceSaveDto.getDose() + "_"
|
||||
+ adviceSaveDto.getMethodCode() + "_"
|
||||
+ adviceSaveDto.getRateCode();
|
||||
if (longUniqueKeySet.contains(uniqueKey)) {
|
||||
log.warn("防重复保存:检测到重复长期医嘱(跨操作类型),跳过 - patientId={}, encounterId={}, adviceDefinitionId={}, dbOpType={}",
|
||||
if (DbOpType.INSERT.getCode().equals(adviceSaveDto.getDbOpType()) && longUniqueKeySet.contains(uniqueKey)) {
|
||||
log.warn("防重复保存:检测到重复长期医嘱,跳过保存 - patientId={}, encounterId={}, adviceDefinitionId={}, dose={}",
|
||||
adviceSaveDto.getPatientId(), adviceSaveDto.getEncounterId(),
|
||||
adviceSaveDto.getAdviceDefinitionId(), adviceSaveDto.getDbOpType());
|
||||
adviceSaveDto.getAdviceDefinitionId(), adviceSaveDto.getDose());
|
||||
continue;
|
||||
}
|
||||
longUniqueKeySet.add(uniqueKey);
|
||||
@@ -429,7 +429,7 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
&& (DbOpType.INSERT.getCode().equals(e.getDbOpType()) || DbOpType.UPDATE.getCode().equals(e.getDbOpType())))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 防重复保存:对所有医嘱进行去重(包括 INSERT 和 UPDATE 混合场景),避免签发时产生重复记录
|
||||
// 防重复保存:对新增医嘱进行去重
|
||||
Set<String> tempUniqueKeySet = new HashSet<>();
|
||||
List<RegAdviceSaveDto> tempUniqueList = new ArrayList<>();
|
||||
for (RegAdviceSaveDto adviceSaveDto : tempInsertOrUpdateList) {
|
||||
@@ -439,10 +439,10 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
+ adviceSaveDto.getDose() + "_"
|
||||
+ adviceSaveDto.getMethodCode() + "_"
|
||||
+ adviceSaveDto.getRateCode();
|
||||
if (tempUniqueKeySet.contains(uniqueKey)) {
|
||||
log.warn("防重复保存:检测到重复临时医嘱(跨操作类型),跳过 - patientId={}, encounterId={}, adviceDefinitionId={}, dbOpType={}",
|
||||
if (DbOpType.INSERT.getCode().equals(adviceSaveDto.getDbOpType()) && tempUniqueKeySet.contains(uniqueKey)) {
|
||||
log.warn("防重复保存:检测到重复临时医嘱,跳过保存 - patientId={}, encounterId={}, adviceDefinitionId={}, dose={}",
|
||||
adviceSaveDto.getPatientId(), adviceSaveDto.getEncounterId(),
|
||||
adviceSaveDto.getAdviceDefinitionId(), adviceSaveDto.getDbOpType());
|
||||
adviceSaveDto.getAdviceDefinitionId(), adviceSaveDto.getDose());
|
||||
continue;
|
||||
}
|
||||
tempUniqueKeySet.add(uniqueKey);
|
||||
@@ -710,21 +710,11 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
|
||||
|
||||
// 批量更新诊疗医嘱状态(使用 update 确保状态字段必定更新)
|
||||
if (!processedRequestIds.isEmpty()) {
|
||||
// 🔧 Bug #487 修复:签发时额外设置 authoredTime,确保签发时间被记录
|
||||
if (is_sign) {
|
||||
iServiceRequestService.update(null,
|
||||
new LambdaUpdateWrapper<ServiceRequest>()
|
||||
.set(ServiceRequest::getStatusEnum, RequestStatus.ACTIVE.getValue())
|
||||
.set(ServiceRequest::getAuthoredTime, authoredTime)
|
||||
.set(ServiceRequest::getSignCode, signCode)
|
||||
.set(ServiceRequest::getStatusEnum,
|
||||
is_save ? RequestStatus.DRAFT.getValue() : RequestStatus.ACTIVE.getValue())
|
||||
.in(ServiceRequest::getId, processedRequestIds));
|
||||
log.info("签发诊疗医嘱成功,requestIds: {}, signCode: {}", processedRequestIds, signCode);
|
||||
} else {
|
||||
iServiceRequestService.update(null,
|
||||
new LambdaUpdateWrapper<ServiceRequest>()
|
||||
.set(ServiceRequest::getStatusEnum, RequestStatus.DRAFT.getValue())
|
||||
.in(ServiceRequest::getId, processedRequestIds));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,10 +76,6 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> saveRequestForm(RequestFormSaveDto requestFormSaveDto, String typeCode) {
|
||||
// 申请单ID(前端空字符串可能反序列化为0L,需同时判0)
|
||||
Long requestFormId = requestFormSaveDto.getRequestFormId();
|
||||
boolean isEdit = requestFormId != null && requestFormId != 0L;
|
||||
|
||||
// 诊疗执行科室配置校验(必须在任何数据库操作之前)
|
||||
List<ActivityOrganizationConfigDto> activityOrganizationConfig =
|
||||
requestFormManageAppMapper.getActivityOrganizationConfig(typeCode);
|
||||
@@ -87,23 +83,12 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
throw new ServiceException("请先配置当前时间段的执行科室");
|
||||
}
|
||||
|
||||
// 逐个校验activityList中的项目是否都配置了执行科室,避免部分通过后在循环中抛异常导致事务复杂化
|
||||
List<ActivitySaveDto> activityList = requestFormSaveDto.getActivityList();
|
||||
if (activityList != null && !activityList.isEmpty()) {
|
||||
for (ActivitySaveDto activitySaveDto : activityList) {
|
||||
Long positionId = activityOrganizationConfig.stream()
|
||||
.filter(dto -> activitySaveDto.getAdviceDefinitionId().equals(dto.getActivityDefinitionId()))
|
||||
.map(ActivityOrganizationConfigDto::getOrganizationId).findFirst().orElse(null);
|
||||
if (positionId == null) {
|
||||
throw new ServiceException(activitySaveDto.getAdviceDefinitionName() + "未配置当前时间段的执行科室");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 诊疗处方号
|
||||
String prescriptionNo;
|
||||
// 申请单ID
|
||||
Long requestFormId = requestFormSaveDto.getRequestFormId();
|
||||
// 编辑场景
|
||||
if (isEdit) {
|
||||
if (requestFormId != null) {
|
||||
RequestForm requestFormInfo = iRequestFormService.getById(requestFormId);
|
||||
prescriptionNo = requestFormInfo.getPrescriptionNo();
|
||||
// 该申请单存在的待发送医嘱个数
|
||||
@@ -147,7 +132,7 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
iRequestFormService.saveOrUpdate(requestForm);
|
||||
|
||||
// 编辑场景时,先删除掉原有诊疗项目及账单再新增
|
||||
if (isEdit) {
|
||||
if (requestFormId != null) {
|
||||
List<Long> serviceRequestIds = iServiceRequestService
|
||||
.list(new LambdaQueryWrapper<ServiceRequest>().eq(ServiceRequest::getPrescriptionNo, prescriptionNo))
|
||||
.stream().map(ServiceRequest::getId).collect(Collectors.toList());
|
||||
@@ -161,6 +146,8 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
|
||||
ServiceRequest serviceRequest;
|
||||
ChargeItem chargeItem;
|
||||
// 诊疗集合
|
||||
List<ActivitySaveDto> activityList = requestFormSaveDto.getActivityList();
|
||||
log.info("保存申请单,typeCode={}, activityListSize={}, encounterId={}", typeCode, activityList != null ? activityList.size() : 0, encounterId);
|
||||
|
||||
for (ActivitySaveDto activitySaveDto : activityList) {
|
||||
|
||||
@@ -82,23 +82,14 @@ public class RequestFormManageController {
|
||||
* 查询检查申请单
|
||||
*
|
||||
* @param encounterId 就诊id
|
||||
* @param startDate 开始日期(可选,格式:yyyy-MM-dd)
|
||||
* @param endDate 结束日期(可选,格式:yyyy-MM-dd)
|
||||
* @param status 单据状态(可选)
|
||||
* @param keyword 关键字(可选,申请单号/检查项目名称模糊匹配)
|
||||
* @return 检查申请单
|
||||
*/
|
||||
@GetMapping(value = "/get-check")
|
||||
public R<?> getCheckRequestForm(
|
||||
@RequestParam(required = false) Long encounterId,
|
||||
@RequestParam(required = false) String startDate,
|
||||
@RequestParam(required = false) String endDate,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String keyword) {
|
||||
public R<?> getCheckRequestForm(@RequestParam(required = false) Long encounterId) {
|
||||
if (encounterId == null) {
|
||||
return R.fail("就诊ID不能为空");
|
||||
}
|
||||
return R.ok(iRequestFormManageAppService.getRequestForm(encounterId, ActivityDefCategory.TEST.getCode(), startDate, endDate, status, keyword));
|
||||
return R.ok(iRequestFormManageAppService.getRequestForm(encounterId, ActivityDefCategory.TEST.getCode()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -138,12 +138,4 @@ public class CurrentDayEncounterTencentDto {
|
||||
*/
|
||||
private String englishName;
|
||||
|
||||
/** 号源池ID(用于分诊队列 div_log 审计日志) */
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long poolId;
|
||||
|
||||
/** 号源槽位ID(用于分诊队列 div_log 审计日志) */
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long slotId;
|
||||
|
||||
}
|
||||
|
||||
@@ -89,12 +89,15 @@
|
||||
cs.apply_doctor_name AS apply_doctor_name,
|
||||
drf.create_time AS apply_time,
|
||||
os.surgery_nature AS surgeryType,
|
||||
os.fee_type AS feeType,
|
||||
fc.contract_name AS feeType,
|
||||
COALESCE(pi.identifier_no, ap.bus_no, '') AS identifierNo
|
||||
FROM op_schedule os
|
||||
LEFT JOIN adm_patient ap ON os.patient_id = ap.id
|
||||
INNER JOIN cli_surgery cs ON os.oper_code = cs.surgery_no AND cs.delete_flag = '0'
|
||||
LEFT JOIN adm_organization o ON cs.org_id = o.id
|
||||
LEFT JOIN adm_encounter ae ON ae.id = cs.encounter_id AND ae.delete_flag = '0'
|
||||
LEFT JOIN adm_account aa ON aa.encounter_id = ae.id AND aa.delete_flag = '0'
|
||||
LEFT JOIN fin_contract fc ON fc.bus_no = aa.contract_no AND fc.delete_flag = '0'
|
||||
LEFT JOIN doc_request_form drf ON drf.prescription_no=cs.surgery_no
|
||||
LEFT JOIN (
|
||||
SELECT patient_id, identifier_no
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
ii.performer_check_id,
|
||||
ii.category_code,
|
||||
ii.dispense_status
|
||||
FROM (( SELECT DISTINCT T1.encounter_id,
|
||||
FROM (( SELECT T1.encounter_id,
|
||||
T1.tenant_id,
|
||||
#{medMedicationRequest} AS advice_table,
|
||||
T1.id AS request_id,
|
||||
@@ -293,7 +293,7 @@
|
||||
T1.sort_number,
|
||||
T1.group_id )
|
||||
UNION
|
||||
( SELECT DISTINCT T1.encounter_id,
|
||||
( SELECT T1.encounter_id,
|
||||
T1.tenant_id,
|
||||
#{worServiceRequest} AS advice_table,
|
||||
T1.id AS request_id,
|
||||
|
||||
@@ -27,9 +27,7 @@
|
||||
T9.payment_id,
|
||||
T9.picture_url,
|
||||
T9.birth_date,
|
||||
t9.english_name,
|
||||
t9.slot_id,
|
||||
t9.pool_id
|
||||
t9.english_name
|
||||
from (
|
||||
SELECT T1.tenant_id AS tenant_id,
|
||||
T1.id AS encounter_id,
|
||||
@@ -53,9 +51,7 @@
|
||||
T13.id AS payment_id,
|
||||
ai.picture_url AS picture_url,
|
||||
T8.birth_date AS birth_date,
|
||||
tx.staff_english_name AS english_name,
|
||||
om_slot.slot_id AS slot_id,
|
||||
om_slot.pool_id AS pool_id
|
||||
tx.staff_english_name AS english_name
|
||||
FROM adm_encounter AS T1
|
||||
LEFT JOIN adm_organization AS T2 ON T1.organization_id = T2.ID AND T2.delete_flag = '0'
|
||||
LEFT JOIN adm_healthcare_service AS T3 ON T1.service_type_id = T3.ID AND T3.delete_flag = '0'
|
||||
@@ -95,8 +91,6 @@
|
||||
AND T13.status_enum = ${paymentStatus}
|
||||
LEFT JOIN adm_invoice AS ai
|
||||
ON ai.reconciliation_id = T13.id AND ai.delete_flag = '0'
|
||||
LEFT JOIN order_main AS om ON T1.order_id = om.id AND om.delete_flag = '0'
|
||||
LEFT JOIN adm_schedule_slot AS om_slot ON om.slot_id = om_slot.id
|
||||
WHERE T1.delete_flag = '0'
|
||||
AND T1.class_enum = #{classEnum}
|
||||
AND T10.context_enum = #{register}
|
||||
|
||||
@@ -193,9 +193,6 @@ public class OpSchedule extends HisBaseEntity {
|
||||
/** 外请专家姓名 */
|
||||
private String externalExpertName;
|
||||
|
||||
/** 费用类别 */
|
||||
private String feeType;
|
||||
|
||||
/** 备注信息 */
|
||||
private String remark;
|
||||
|
||||
|
||||
@@ -226,14 +226,8 @@ function getList() {
|
||||
getDiagnosisTreatmentList(queryParams.value).then((res) => {
|
||||
loading.value = false;
|
||||
catagoryList.value = res.data.records.map(record => {
|
||||
// 为每一行初始化 filteredOptions,确保显示框能正确显示项目名称
|
||||
const filteredOptions = allImplementDepartmentList.value.slice(0, 100);
|
||||
// 确保后端返回的项目名称选项存在于 filteredOptions 中,避免 el-select 因找不到选项而回显为 ID
|
||||
if (record.activityDefinitionId && !filteredOptions.some(o => o.value === record.activityDefinitionId)) {
|
||||
filteredOptions.push({
|
||||
value: record.activityDefinitionId,
|
||||
label: record.activityDefinitionId_dictText || record.activityDefinitionId
|
||||
});
|
||||
}
|
||||
return {
|
||||
...record,
|
||||
loading: false,
|
||||
|
||||
@@ -122,37 +122,15 @@ function getList() {
|
||||
return;
|
||||
}
|
||||
|
||||
// 🔧 Bug #448 修复:显式构建请求参数,确保 adviceType 正确传递
|
||||
// 不直接使用 queryParams.value,避免 undefined 值被发送到后端导致过滤失效
|
||||
const requestParams = {
|
||||
pageSize: queryParams.value.pageSize,
|
||||
pageNum: queryParams.value.pageNum,
|
||||
organizationId: props.patientInfo.orgId,
|
||||
};
|
||||
queryParams.value.organizationId = props.patientInfo.orgId;
|
||||
console.log('[adviceBaseList] getList() 请求参数:', JSON.stringify(queryParams.value));
|
||||
|
||||
// 只在 adviceType 有值时添加(0 是无效值,undefined/null 会导致后端查询所有类型)
|
||||
if (queryParams.value.adviceType != null && queryParams.value.adviceType !== 0) {
|
||||
requestParams.adviceType = queryParams.value.adviceType;
|
||||
}
|
||||
|
||||
// 只在 categoryCode 有值时添加
|
||||
if (queryParams.value.categoryCode) {
|
||||
requestParams.categoryCode = queryParams.value.categoryCode;
|
||||
}
|
||||
|
||||
// 只在 searchKey 有值时添加
|
||||
if (queryParams.value.searchKey) {
|
||||
requestParams.searchKey = queryParams.value.searchKey;
|
||||
}
|
||||
|
||||
console.log('[adviceBaseList] getList() 请求参数:', JSON.stringify(requestParams));
|
||||
|
||||
getAdviceBaseInfo(requestParams).then((res) => {
|
||||
getAdviceBaseInfo(queryParams.value).then((res) => {
|
||||
console.log('[adviceBaseList] getList() 响应数据:', {
|
||||
total: res.data?.total,
|
||||
recordsCount: res.data?.records?.length || 0,
|
||||
firstRecord: res.data?.records?.[0]?.adviceName || '无数据',
|
||||
adviceType: requestParams.adviceType
|
||||
adviceType: queryParams.value.adviceType
|
||||
});
|
||||
adviceBaseList.value = res.data.records || [];
|
||||
total.value = res.data.total || 0;
|
||||
|
||||
@@ -887,10 +887,6 @@ function handleDelete() {
|
||||
if (item.statusEnum != 1 || item.chargeStatus == 5) {
|
||||
return null;
|
||||
}
|
||||
// 🔧 Bug #442: 非本人创建的医嘱不允许删除(与签发/签退逻辑保持一致)
|
||||
if (Number(item.bizRequestFlag) !== 1 && item.bizRequestFlag) {
|
||||
return null;
|
||||
}
|
||||
// 🔧 Bug #442: 已保存的行必须有有效的 requestId,否则跳过(避免后端删除不存在的记录)
|
||||
if (item.requestId == null || item.requestId === undefined || item.requestId === '') {
|
||||
return null;
|
||||
@@ -900,7 +896,7 @@ function handleDelete() {
|
||||
dbOpType: '3',
|
||||
adviceType: item.adviceType,
|
||||
};
|
||||
}).filter(item => item !== null); // 过滤掉已签发、已收费、非本人创建或无 requestId 的项目
|
||||
}).filter(item => item !== null); // 过滤掉已签发、已收费或无 requestId 的项目
|
||||
|
||||
if (deleteList.length == 0) {
|
||||
proxy.$modal.msgWarning('只能删除待签发且未收费的项目');
|
||||
@@ -1036,14 +1032,6 @@ function handleSave() {
|
||||
requestId: item.requestId,
|
||||
dbOpType: '1',
|
||||
groupId: item.groupId,
|
||||
// 🔧 Bug #443: 补充顶层关键字段(这些不在 contentJson 中,需从 API 响应顶层提取)
|
||||
encounterId: item.encounterId,
|
||||
patientId: item.patientId,
|
||||
locationId: item.positionId,
|
||||
adviceType: item.adviceType,
|
||||
adviceTableName: item.adviceTableName,
|
||||
adviceDefinitionId: item.adviceDefinitionId,
|
||||
chargeItemId: item.chargeItemId,
|
||||
};
|
||||
});
|
||||
savePrescriptionSign({
|
||||
|
||||
@@ -1366,9 +1366,6 @@ async function buildSubmitData() {
|
||||
} else if (formData.otherDisease) {
|
||||
// 其他传染病使用自定义编码
|
||||
diseaseCode = 'OTHER';
|
||||
} else if (formData.selectedDiseases && formData.selectedDiseases.length > 0) {
|
||||
// 兜底:如果 ClassA/B/C 都为空但 selectedDiseases 有值,取第一个作为 diseaseCode
|
||||
diseaseCode = formData.selectedDiseases[0];
|
||||
}
|
||||
|
||||
// 转换年龄单位:岁=1, 月=2, 天=3
|
||||
@@ -1778,33 +1775,6 @@ defineExpose({ show, showReport, close: handleClose });
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 输入框下划线样式(与 underline-select 保持一致) */
|
||||
.underline-input :deep(.el-input__wrapper) {
|
||||
border: none;
|
||||
border-bottom: 1px solid #dcdfe6;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.underline-input :deep(.el-input__wrapper:hover) {
|
||||
border-bottom-color: #c0c4cc;
|
||||
}
|
||||
|
||||
.underline-input :deep(.el-input__wrapper.is-focus) {
|
||||
border-bottom-color: #409eff;
|
||||
}
|
||||
|
||||
.underline-input :deep(.el-input__inner) {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.underline-input :deep(.el-input__inner::placeholder) {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 街道下拉框下划线样式 */
|
||||
.underline-select {
|
||||
width: 100%;
|
||||
|
||||
@@ -3,56 +3,15 @@
|
||||
<!-- ====== 顶部卡片:申请单列表 ====== -->
|
||||
<div class="top-section">
|
||||
<div class="section-header">
|
||||
<span class="section-title">检查项目 ({{ filteredApplicationList.length }})</span>
|
||||
<span class="section-title">检查项目 ({{ applicationList.length }})</span>
|
||||
<div class="header-actions">
|
||||
<el-button type="primary" @click="handleAdd" icon="Plus">新增</el-button>
|
||||
<el-button type="success" @click="handleSave" icon="Finished">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bug #499: 查询过滤工具栏 -->
|
||||
<div class="search-toolbar">
|
||||
<el-form :inline="true" size="small">
|
||||
<el-form-item label="日期范围">
|
||||
<el-date-picker
|
||||
v-model="searchForm.dateRange"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="searchForm.applyStatus" placeholder="全部" clearable style="width: 140px">
|
||||
<el-option
|
||||
v-for="opt in statusOptions"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="关键字">
|
||||
<el-input
|
||||
v-model="searchForm.keyword"
|
||||
placeholder="申请单号 / 检查项目"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch" icon="Search">搜索</el-button>
|
||||
<el-button @click="handleResetSearch" icon="Refresh">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="filteredApplicationList"
|
||||
:data="applicationList"
|
||||
:max-height="200"
|
||||
highlight-current-row
|
||||
@row-click="handleRowClick"
|
||||
@@ -376,9 +335,8 @@
|
||||
加载中...
|
||||
</div>
|
||||
<!-- Bug #428修复: 渲染分类联动加载的检查方法列表 -->
|
||||
<!-- Bug #500修复: v-if 改为 v-show,避免方法列表加载时 DOM 突然插入导致高度跳变 -->
|
||||
<div
|
||||
v-show="cat.methods && cat.methods.length > 0"
|
||||
v-if="cat.methods && cat.methods.length > 0"
|
||||
class="method-section"
|
||||
>
|
||||
<div class="method-section-title">检查方法</div>
|
||||
@@ -494,74 +452,6 @@ const activeDetailTab = ref('applyForm');
|
||||
const applicationList = ref([]);
|
||||
const selectedItems = ref([]);
|
||||
|
||||
// Bug #499: 查询过滤状态
|
||||
const searchForm = reactive({
|
||||
dateRange: [],
|
||||
applyStatus: '',
|
||||
keyword: ''
|
||||
});
|
||||
|
||||
// 申请单状态选项
|
||||
const statusOptions = [
|
||||
{ label: '已开单', value: 0 },
|
||||
{ label: '已收费', value: 1 },
|
||||
{ label: '已预约', value: 2 },
|
||||
{ label: '已签到', value: 3 },
|
||||
{ label: '部分报告', value: 4 },
|
||||
{ label: '已完告', value: 5 },
|
||||
{ label: '已作废', value: 6 }
|
||||
];
|
||||
|
||||
// Bug #499: 过滤后的申请单列表
|
||||
const filteredApplicationList = computed(() => {
|
||||
let result = applicationList.value;
|
||||
|
||||
// 日期范围过滤
|
||||
if (searchForm.dateRange && searchForm.dateRange.length === 2) {
|
||||
const start = searchForm.dateRange[0];
|
||||
const end = searchForm.dateRange[1];
|
||||
result = result.filter(item => {
|
||||
const d = item.applyTime;
|
||||
if (!d) return false;
|
||||
const dateStr = d.length > 10 ? d.substring(0, 10) : d;
|
||||
return dateStr >= start && dateStr <= end;
|
||||
});
|
||||
}
|
||||
|
||||
// 状态过滤
|
||||
if (searchForm.applyStatus !== '' && searchForm.applyStatus !== null && searchForm.applyStatus !== undefined) {
|
||||
result = result.filter(item => item.applyStatus === searchForm.applyStatus);
|
||||
}
|
||||
|
||||
// 关键字过滤(申请单号、申检部位、检查项目名)
|
||||
if (searchForm.keyword) {
|
||||
const kw = searchForm.keyword.toLowerCase();
|
||||
result = result.filter(item => {
|
||||
return (item.applyNo || '').toLowerCase().includes(kw)
|
||||
|| (item.inspectionArea || '').toLowerCase().includes(kw);
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
// Bug #499: 搜索与重置
|
||||
function handleSearch() {
|
||||
// 过滤逻辑由 computed 自动处理
|
||||
}
|
||||
|
||||
function handleResetSearch() {
|
||||
const now = new Date();
|
||||
const end = now.toISOString().substring(0, 10);
|
||||
const start = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000).toISOString().substring(0, 10);
|
||||
searchForm.dateRange = [start, end];
|
||||
searchForm.applyStatus = '';
|
||||
searchForm.keyword = '';
|
||||
}
|
||||
|
||||
// 初始化默认日期范围为近一周
|
||||
handleResetSearch();
|
||||
|
||||
// 🔧 BugFix#426: 懒加载套餐明细
|
||||
async function loadPackageDetails(row, treeNode, resolve) {
|
||||
if (!row.isPackage || !row.packageId) {
|
||||
@@ -683,7 +573,7 @@ const categoryList = ref([]); // 原始分类+项目数据
|
||||
const dictSearchKey = ref('');
|
||||
const activeNames = ref(''); // 当前展开的折叠项
|
||||
const categoryLoadingSet = ref(new Set()); // Bug #500: 正在加载方法的分类集合
|
||||
const currentActiveCategory = ref(null); // Bug #500: 记录当前激活的分类,忽略过期请求响应
|
||||
const isAnimating = ref(false); // Bug #500: 防止快速切换时折叠动画重叠导致抖动
|
||||
|
||||
const allMethods = ref([]);
|
||||
|
||||
@@ -800,18 +690,15 @@ const availableMethods = computed(() => {
|
||||
// 当可选方法列表改变时,如果当前选中的方法不在新列表中,则清空
|
||||
// #428: 分类展开时联动加载检查方法
|
||||
// Bug #500: 使用 categoryLoadingSet 替代 dictLoading,避免切换分类时整个区域闪烁
|
||||
// Bug #500: 添加 currentActiveCategory 守卫,忽略过期请求响应,防止快速切换时数据闪烁
|
||||
async function handleCategoryExpand(cat) {
|
||||
if (!cat || !cat.typeName) return;
|
||||
|
||||
// 如果已加载过或正在加载中,跳过
|
||||
if ((cat.methods && cat.methods.length > 0) || categoryLoadingSet.value.has(cat.typeId)) return;
|
||||
|
||||
categoryLoadingSet.value.add(cat.typeId);
|
||||
currentActiveCategory.value = cat.typeId;
|
||||
try {
|
||||
const res = await searchCheckMethod({ checkType: cat.typeName });
|
||||
// 忽略过期请求:用户已切换到其他分类,丢弃当前响应
|
||||
if (currentActiveCategory.value !== cat.typeId) return;
|
||||
let data = res?.data?.data || res?.data || res?.rows || res;
|
||||
if (!Array.isArray(data) && res?.data && Array.isArray(res.data.data)) {
|
||||
data = res.data.data;
|
||||
@@ -830,16 +717,17 @@ async function handleCategoryExpand(cat) {
|
||||
}));
|
||||
}
|
||||
} catch (err) {
|
||||
if (currentActiveCategory.value !== cat.typeId) return;
|
||||
console.error('加载分类检查方法失败', err);
|
||||
} finally {
|
||||
categoryLoadingSet.value.delete(cat.typeId);
|
||||
}
|
||||
}
|
||||
// Bug #500修复: 不阻塞 accordion 状态更新,仅防止重复加载同一分类的方法
|
||||
// Bug #500: 添加防抖逻辑,快速切换时跳过中间状态的动画,避免高度跳变和白屏闪烁
|
||||
function handleCollapseChange(activeName) {
|
||||
// 始终记录当前激活的分类,确保 handleCategoryExpand 能正确忽略过期请求
|
||||
currentActiveCategory.value = activeName || null;
|
||||
if (isAnimating.value) return; // 动画进行中,忽略后续点击
|
||||
|
||||
isAnimating.value = true;
|
||||
setTimeout(() => { isAnimating.value = false; }, 300); // 与 CSS 过渡时长一致
|
||||
|
||||
if (activeName) {
|
||||
// Bug #428修复: 直接从 categoryList(原始响应式数组)查找分类,
|
||||
@@ -1555,19 +1443,6 @@ defineExpose({ getList });
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* Bug #499: 查询过滤工具栏 */
|
||||
.search-toolbar {
|
||||
margin-bottom: 10px;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
.search-toolbar :deep(.el-form-item) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.search-toolbar :deep(.el-form-item__label) {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 底部区域:左表单 + 右分类 */
|
||||
.bottom-section {
|
||||
display: flex;
|
||||
@@ -1645,7 +1520,6 @@ defineExpose({ getList });
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden; /* Bug #500: 防止切换时水平方向溢出导致抖动 */
|
||||
min-height: 120px; /* Bug #500: 固定最小高度,避免分类切换时 flex 容器高度突变 */
|
||||
}
|
||||
.empty-hint {
|
||||
color: #909399;
|
||||
@@ -1918,10 +1792,10 @@ defineExpose({ getList });
|
||||
height: auto;
|
||||
line-height: 1.5;
|
||||
}
|
||||
/* Bug #500修复: 折叠内容使用明确属性过渡,避免 transition: all 导致子元素意外动画 */
|
||||
/* Bug #500: 折叠内容添加平滑过渡动画,避免切换时高度跳变 */
|
||||
:deep(.el-collapse-item__content) {
|
||||
padding-bottom: 4px;
|
||||
transition: height 0.3s ease, max-height 0.3s ease;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
/* Bug #500: 折叠面板动画容器,添加 overflow:hidden 防止展开时内容溢出导致闪烁 */
|
||||
:deep(.el-collapse-item__wrap) {
|
||||
|
||||
@@ -589,46 +589,36 @@ function handleUseOrderGroup(row) {
|
||||
minUnitPrice: orderDetail.minUnitPrice,
|
||||
inventoryList: orderDetail.inventoryList || [],
|
||||
priceList: orderDetail.priceList || [],
|
||||
partPercent: orderDetail.partPercent ?? 1,
|
||||
partAttributeEnum: orderDetail.partAttributeEnum,
|
||||
unitConversionRatio: orderDetail.unitConversionRatio,
|
||||
partPercent: orderDetail.partPercent || 1,
|
||||
// 🔧 Bug #218 修复:positionId 可能存储在 item 本身,优先使用 item.positionId
|
||||
positionId: item.positionId ?? orderDetail.positionId,
|
||||
positionId: item.positionId || orderDetail.positionId,
|
||||
defaultLotNumber: orderDetail.defaultLotNumber,
|
||||
|
||||
// 单位信息
|
||||
unitCode: item.unitCode ?? orderDetail.unitCode,
|
||||
categoryCode: item.categoryCode ?? orderDetail.categoryCode,
|
||||
unitCode: item.unitCode || orderDetail.unitCode,
|
||||
unitCodeName: item.unitCodeName || orderDetail.unitCode_dictText,
|
||||
minUnitCode: orderDetail.minUnitCode,
|
||||
doseUnitCode: orderDetail.doseUnitCode,
|
||||
|
||||
// 合并后的完整对象(用于 setValue)
|
||||
// 先展开 orderDetail 获取所有药品基础字段(categoryCode、minUnitCode、doseUnitCode、
|
||||
// partPercent、partAttributeEnum、unitConversionRatio、defaultLotNumber 等),
|
||||
// 再用组套用户覆盖值覆盖,确保单次剂量/频次/用法/用药天数/总量等不被丢失
|
||||
mergedDetail: {
|
||||
...orderDetail,
|
||||
adviceName: orderDetail.adviceName || item.orderDefinitionName || '未知项目',
|
||||
adviceType: orderDetail.adviceType,
|
||||
quantity: item.quantity,
|
||||
unitCode: item.unitCode ?? orderDetail.unitCode,
|
||||
categoryCode: item.categoryCode ?? orderDetail.categoryCode,
|
||||
unitCode: item.unitCode || orderDetail.unitCode,
|
||||
unitCodeName: item.unitCodeName,
|
||||
dose: item.dose ?? orderDetail.dose,
|
||||
rateCode: item.rateCode ?? orderDetail.rateCode,
|
||||
methodCode: item.methodCode ?? orderDetail.methodCode,
|
||||
dispensePerDuration: item.dispensePerDuration ?? orderDetail.dispensePerDuration,
|
||||
doseQuantity: item.doseQuantity ?? orderDetail.doseQuantity,
|
||||
// 🔧 Bug #218 / #403 修复:positionId 可能存储在 item 本身,优先使用 item.positionId
|
||||
positionId: item.positionId ?? orderDetail.positionId,
|
||||
// 执行科室:优先使用组套明细中保存的 orgId
|
||||
orgId: item.orgId ?? orderDetail.orgId,
|
||||
orgName: item.orgName ?? orderDetail.orgName,
|
||||
// 组号(保留组套中的分组信息)
|
||||
groupId: item.groupId,
|
||||
groupOrder: item.groupOrder,
|
||||
therapyEnum: item.therapyEnum ?? orderDetail.therapyEnum ?? '1',
|
||||
dose: item.dose || orderDetail.dose,
|
||||
rateCode: item.rateCode || orderDetail.rateCode,
|
||||
methodCode: item.methodCode || orderDetail.methodCode,
|
||||
dispensePerDuration: item.dispensePerDuration || orderDetail.dispensePerDuration,
|
||||
doseQuantity: item.doseQuantity,
|
||||
inventoryList: orderDetail.inventoryList || [],
|
||||
priceList: orderDetail.priceList || [],
|
||||
partPercent: orderDetail.partPercent || 1,
|
||||
// 🔧 Bug #218 修复:positionId 可能存储在 item 本身,优先使用 item.positionId
|
||||
positionId: item.positionId || orderDetail.positionId,
|
||||
defaultLotNumber: orderDetail.defaultLotNumber,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1133,17 +1133,13 @@ function submitForm() {
|
||||
// 新增手术
|
||||
addSurgery(form.value).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess('手术申请提交成功!')
|
||||
proxy.$modal.msgSuccess(res.msg || '手术申请提交成功!')
|
||||
// 保存麻醉方式
|
||||
sessionStorage.setItem('anesthesiaType', form.value.anesthesiaTypeEnum)
|
||||
open.value = false
|
||||
emit('saved') // 通知父组件刷新医嘱列表
|
||||
// 刷新手术申请列表,使用 nextTick 确保数据一致性
|
||||
proxy.$nextTick(() => {
|
||||
if (props.patientInfo?.encounterId) {
|
||||
// 刷新手术申请列表
|
||||
getList()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
proxy.$modal.msgError(res.msg || '新增手术失败,请检查表单信息')
|
||||
}
|
||||
@@ -1155,17 +1151,13 @@ function submitForm() {
|
||||
// 修改手术
|
||||
updateSurgery(form.value).then((res) => {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess('手术申请修改成功!')
|
||||
proxy.$modal.msgSuccess(res.msg || '手术申请修改成功!')
|
||||
// 保存麻醉方式
|
||||
sessionStorage.setItem('anesthesiaType', form.value.anesthesiaTypeEnum)
|
||||
open.value = false
|
||||
emit('saved') // 通知父组件刷新医嘱列表
|
||||
// 刷新手术申请列表,使用 nextTick 确保数据一致性
|
||||
proxy.$nextTick(() => {
|
||||
if (props.patientInfo?.encounterId) {
|
||||
// 刷新手术申请列表
|
||||
getList()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
proxy.$modal.msgError(res.msg || '更新手术失败,请检查表单信息')
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ const currentSelectRow = ref<any>({});
|
||||
const queryParams = ref({
|
||||
pageSize: 100,
|
||||
pageNo: 1,
|
||||
adviceTypes: [1, 2, 3, 6],
|
||||
adviceTypes: '1,2,3,6',
|
||||
searchKey: '',
|
||||
organizationId: '',
|
||||
categoryCode: '',
|
||||
@@ -88,10 +88,10 @@ const tableColumns = computed<TableColumn[]>(() => [
|
||||
function refresh(adviceType: any, categoryCode: string, searchKey: string) {
|
||||
// 有搜索词时跨类型搜索,避免用户输入"级护理"但因当前adviceType为药品而搜不到诊疗类护理项目
|
||||
if (searchKey) {
|
||||
queryParams.value.adviceTypes = [1, 2, 3, 6];
|
||||
queryParams.value.adviceTypes = '1,2,3,6';
|
||||
} else {
|
||||
queryParams.value.adviceTypes =
|
||||
adviceType !== undefined && adviceType !== '' ? [parseInt(adviceType)] : [1, 2, 3, 6];
|
||||
adviceType !== undefined && adviceType !== '' ? String(adviceType) : '1,2,3,6';
|
||||
}
|
||||
queryParams.value.categoryCode = categoryCode || '';
|
||||
queryParams.value.searchKey = searchKey || '';
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
<el-descriptions title="申请单描述" :column="2">
|
||||
<template v-for="(value, key) in descJsonData" :key="key">
|
||||
<el-descriptions-item v-if="isFieldMatched(key)" :label="getFieldLabel(key)">
|
||||
{{ transformField(key, value) || '-' }}
|
||||
{{ value || '-' }}
|
||||
</el-descriptions-item>
|
||||
</template>
|
||||
</el-descriptions>
|
||||
@@ -270,12 +270,6 @@ const parseStatus = (status) => {
|
||||
const labelMap = {
|
||||
categoryType: '项目类别',
|
||||
targetDepartment: '发往科室',
|
||||
urgencyLevel: '紧急程度',
|
||||
allergyHistory: '过敏史',
|
||||
examinationPurpose: '检查目的',
|
||||
expectedExaminationTime: '期望检查时间',
|
||||
medicalHistorySummary: '病史摘要',
|
||||
allergyConfirmed: '过敏确认',
|
||||
symptom: '症状',
|
||||
sign: '体征',
|
||||
clinicalDiagnosis: '临床诊断',
|
||||
@@ -284,17 +278,6 @@ const labelMap = {
|
||||
attention: '注意事项',
|
||||
};
|
||||
|
||||
// Fields that need value transformation before display
|
||||
const transformField = (key, value) => {
|
||||
if (key === 'urgencyLevel') {
|
||||
return value === 'emergency' ? '急诊' : '普通';
|
||||
}
|
||||
if (key === 'allergyConfirmed') {
|
||||
return value === true || value === 'true' ? '已口头确认' : '未确认';
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
const isFieldMatched = (key) => {
|
||||
return key in labelMap;
|
||||
};
|
||||
@@ -309,45 +292,50 @@ const hasMatchedFields = computed(() => {
|
||||
});
|
||||
|
||||
/** 查询科室 */
|
||||
const getLocationInfo = async () => {
|
||||
try {
|
||||
const res = await getDepartmentList();
|
||||
orgOptions.value = Array.isArray(res.data) ? res.data : [];
|
||||
} catch (e) {
|
||||
console.warn('科室列表加载失败:', e.message);
|
||||
orgOptions.value = [];
|
||||
}
|
||||
const getLocationInfo = () => {
|
||||
getDepartmentList().then((res) => {
|
||||
orgOptions.value = res.data || [];
|
||||
});
|
||||
};
|
||||
|
||||
// 递归查找树形科室节点
|
||||
const findTreeItem = (list, id) => {
|
||||
if (!list || list.length === 0) return null;
|
||||
for (const item of list) {
|
||||
if (item.id == id) return item;
|
||||
if (item.children && item.children.length > 0) {
|
||||
const found = findTreeItem(item.children, id);
|
||||
if (found) return found;
|
||||
const recursionFun = (targetDepartment) => {
|
||||
let name = '';
|
||||
for (let index = 0; index < orgOptions.value.length; index++) {
|
||||
const obj = orgOptions.value[index];
|
||||
if (obj.id == targetDepartment) {
|
||||
name = obj.name;
|
||||
}
|
||||
const subObjArray = obj['children'];
|
||||
if (subObjArray && subObjArray.length > 0) {
|
||||
for (let index = 0; index < subObjArray.length; index++) {
|
||||
const item = subObjArray[index];
|
||||
if (item.id == targetDepartment) {
|
||||
name = item.name;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
};
|
||||
|
||||
const handleViewDetail = async (row) => {
|
||||
// 确保科室数据已加载,以便将 ID 解析为名称
|
||||
if (!orgOptions.value || orgOptions.value.length === 0) {
|
||||
await getLocationInfo();
|
||||
}
|
||||
console.log('targetDepartment========>', JSON.stringify(row));
|
||||
|
||||
currentDetail.value = row;
|
||||
// 解析 descJson
|
||||
if (row.descJson) {
|
||||
try {
|
||||
const obj = JSON.parse(row.descJson);
|
||||
// 将发往科室 ID 转换为名称
|
||||
if (obj.targetDepartment) {
|
||||
const deptItem = findTreeItem(orgOptions.value, obj.targetDepartment);
|
||||
obj.targetDepartment = deptItem ? deptItem.name : obj.targetDepartment;
|
||||
// 确保科室数据已加载
|
||||
if (!orgOptions.value || orgOptions.value.length === 0) {
|
||||
await new Promise((resolve) => {
|
||||
getDepartmentList().then((res) => {
|
||||
orgOptions.value = res.data || [];
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
obj.targetDepartment = recursionFun(obj.targetDepartment);
|
||||
descJsonData.value = obj;
|
||||
} catch (e) {
|
||||
console.error('解析 descJson 失败:', e);
|
||||
|
||||
@@ -82,11 +82,7 @@
|
||||
</template>
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="patientName" label="患者姓名" width="120" />
|
||||
<el-table-column label="申请单名称" width="140">
|
||||
<template #default="scope">
|
||||
<span>{{ buildApplicationName(scope.row) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="申请单名称" width="140" />
|
||||
<el-table-column prop="createTime" label="创建时间" width="160" />
|
||||
<el-table-column prop="prescriptionNo" label="申请单号" width="140" />
|
||||
<el-table-column label="单据状态" width="100" align="center">
|
||||
@@ -107,11 +103,11 @@
|
||||
<el-table-column prop="requesterId_dictText" label="申请者" width="120" />
|
||||
<el-table-column label="操作" align="center" fixed="right" width="160">
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.status == 0">
|
||||
<template v-if="scope.row.billStatus == 0 || scope.row.status == 0">
|
||||
<el-button link type="primary" @click="handleEdit(scope.row)">修改</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
<template v-else-if="scope.row.status == 1">
|
||||
<template v-else-if="scope.row.billStatus == 1 || scope.row.status == 1">
|
||||
<el-button link type="warning" @click="handleWithdraw(scope.row)">撤回</el-button>
|
||||
</template>
|
||||
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
|
||||
@@ -141,7 +137,7 @@
|
||||
<el-descriptions-item label="创建时间">{{
|
||||
currentDetail.createTime || '-'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请单号">{{
|
||||
<el-descriptions-item label="处方号">{{
|
||||
currentDetail.prescriptionNo || '-'
|
||||
}}</el-descriptions-item>
|
||||
<el-descriptions-item label="申请者">{{
|
||||
@@ -191,7 +187,7 @@ import {computed, getCurrentInstance, ref, watch} from 'vue';
|
||||
import {Refresh, Search} from '@element-plus/icons-vue';
|
||||
import {patientInfo} from '../../store/patient.js';
|
||||
import {getInspection, deleteRequestForm, withdrawRequestForm} from './api';
|
||||
import {getDepartmentList} from '@/api/public.js';
|
||||
import {getOrgList} from '@/views/doctorstation/components/api.js';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
@@ -289,9 +285,6 @@ const labelMap = {
|
||||
otherDiagnosis: '其他诊断',
|
||||
relatedResult: '相关结果',
|
||||
attention: '注意事项',
|
||||
applicationType: '申请类型',
|
||||
specimenName: '标本类型',
|
||||
executeTime: '执行时间',
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -318,8 +311,8 @@ const parsePriorityCode = (descJson) => {
|
||||
if (!descJson) return '-';
|
||||
try {
|
||||
const obj = JSON.parse(descJson);
|
||||
// applicationType: 0-普通, 1-急诊
|
||||
return obj.applicationType === 1 ? '急' : '普通';
|
||||
// priorityCode: 0-普通, 1-急
|
||||
return obj.priorityCode === 1 ? '急' : '普通';
|
||||
} catch (e) {
|
||||
console.error('解析 descJson 失败:', e);
|
||||
return '-';
|
||||
@@ -343,24 +336,6 @@ const parseSpecimenType = (descJson) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据申请单详情构建申请单名称
|
||||
* 单一项目:显示项目名称+数量
|
||||
* 多个项目:显示首个项目名称+数量+"等X项"
|
||||
*/
|
||||
const buildApplicationName = (row) => {
|
||||
const details = row.requestFormDetailList;
|
||||
if (!details || details.length === 0) {
|
||||
return row.name || '-';
|
||||
}
|
||||
if (details.length === 1) {
|
||||
const item = details[0];
|
||||
return `${item.adviceName}${item.quantity || ''}`;
|
||||
}
|
||||
const first = details[0];
|
||||
return `${first.adviceName}${first.quantity || ''}等${details.length}项`;
|
||||
};
|
||||
|
||||
const isFieldMatched = (key) => {
|
||||
return key in labelMap;
|
||||
};
|
||||
@@ -416,9 +391,6 @@ const handleViewDetail = async (row) => {
|
||||
try {
|
||||
const obj = JSON.parse(row.descJson);
|
||||
obj.targetDepartment = recursionFun(obj.targetDepartment);
|
||||
// 转换申请类型编码为可读文本
|
||||
if (obj.applicationType === 0) obj.applicationType = '普通';
|
||||
else if (obj.applicationType === 1) obj.applicationType = '急诊';
|
||||
descJsonData.value = obj;
|
||||
} catch (e) {
|
||||
console.error('解析 descJson 失败:', e);
|
||||
|
||||
@@ -144,40 +144,23 @@ const applicationListAll = ref();
|
||||
const applicationList = ref();
|
||||
const loading = ref(false);
|
||||
const orgOptions = ref([]); // 科室选项
|
||||
const getList = async () => {
|
||||
const getList = () => {
|
||||
if (!patientInfo.value?.inHospitalOrgId) {
|
||||
applicationList.value = [];
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const allRecords = [];
|
||||
let currentPage = 1;
|
||||
const pageSize = 500;
|
||||
|
||||
// 分页拉取全部数据(后端单页最多500条)
|
||||
while (true) {
|
||||
const res = await getApplicationList({
|
||||
pageSize,
|
||||
pageNo: currentPage,
|
||||
getApplicationList({
|
||||
pageSize: 9999,
|
||||
pageNum: 1,
|
||||
categoryCode: '22',
|
||||
organizationId: patientInfo.value.inHospitalOrgId,
|
||||
adviceTypes: [3], //1 药品 2耗材 3诊疗
|
||||
});
|
||||
if (res.code !== 200) {
|
||||
proxy.$message.error(res.message);
|
||||
applicationList.value = [];
|
||||
return;
|
||||
}
|
||||
const records = res.data?.records || [];
|
||||
allRecords.push(...records);
|
||||
// 当前页不足 pageSize 或已无数据,说明已全部拉取
|
||||
if (records.length < pageSize) break;
|
||||
currentPage++;
|
||||
}
|
||||
|
||||
applicationListAll.value = allRecords;
|
||||
applicationList.value = allRecords.map((item) => {
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
applicationListAll.value = res.data.records;
|
||||
applicationList.value = res.data.records.map((item) => {
|
||||
const priceInfo = item.priceList?.[0] || {};
|
||||
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
|
||||
const unit = item.unitCode_dictText || item.unitCode || '';
|
||||
@@ -188,12 +171,15 @@ const getList = async () => {
|
||||
key: item.adviceDefinitionId,
|
||||
};
|
||||
});
|
||||
} catch (e) {
|
||||
proxy.$message.error('获取检验项目列表失败');
|
||||
console.log('applicationList========>', JSON.stringify(res.data.records));
|
||||
} else {
|
||||
proxy.$message.error(res.message);
|
||||
applicationList.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
const transferValue = ref([]);
|
||||
const form = reactive({
|
||||
@@ -256,16 +242,12 @@ const projectWithDepartment = (selectProjectIds, type) => {
|
||||
if (type === 2 && manualDept) {
|
||||
form.targetDepartment = manualDept;
|
||||
isRelease = true;
|
||||
} else if (type === 2 && !manualDept) {
|
||||
// 提交时用户未手动选择科室,才提示错误
|
||||
} else {
|
||||
isRelease = false;
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: '未找到项目执行的科室',
|
||||
});
|
||||
} else {
|
||||
// type=1(选择项目变化)时,不弹窗,仅清空科室让用户自行选择
|
||||
isRelease = false;
|
||||
}
|
||||
}
|
||||
if (findItem && isRelease) {
|
||||
|
||||
@@ -483,10 +483,7 @@ const submit = () => {
|
||||
encounterId: patientInfo.value.encounterId,
|
||||
organizationId: patientInfo.value.inHospitalOrgId,
|
||||
requestFormId: '',
|
||||
name: transferValue.value.map(id => {
|
||||
const item = applicationListAll.value?.find(i => i.adviceDefinitionId === id);
|
||||
return item?.adviceName || '';
|
||||
}).filter(Boolean).join('、'),
|
||||
name: applicationListAllFilter.map(item => item.adviceName).join('、'),
|
||||
descJson: JSON.stringify(submitForm),
|
||||
categoryEnum: '2',
|
||||
}).then((res) => {
|
||||
|
||||
@@ -86,9 +86,6 @@ import {getApplicationList, saveSurgery} from './api';
|
||||
import {ElMessage} from 'element-plus';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
// 模块级缓存:避免每次打开弹窗都重新请求手术项目列表
|
||||
let surgeryRecordsCache = null; // 原始 API 记录
|
||||
let surgeryMappedCache = null; // 映射后的 el-transfer 数据
|
||||
// 递归查找树形科室节点
|
||||
const findTreeItem = (list, id) => {
|
||||
if (!list || list.length === 0) return null;
|
||||
@@ -113,12 +110,6 @@ const getList = () => {
|
||||
applicationList.value = [];
|
||||
return;
|
||||
}
|
||||
// 命中缓存时直接使用,避免重复请求导致加载缓慢
|
||||
if (surgeryMappedCache && surgeryMappedCache.length > 0) {
|
||||
applicationList.value = surgeryMappedCache;
|
||||
applicationListAll.value = surgeryRecordsCache;
|
||||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
getApplicationList({
|
||||
pageSize: 500,
|
||||
@@ -141,9 +132,6 @@ const getList = () => {
|
||||
key: item.adviceDefinitionId,
|
||||
};
|
||||
});
|
||||
// 写入模块缓存,后续打开弹窗直接复用
|
||||
surgeryRecordsCache = res.data.records;
|
||||
surgeryMappedCache = applicationList.value;
|
||||
} else {
|
||||
console.warn('获取手术项目列表失败:', res.message);
|
||||
applicationList.value = [];
|
||||
|
||||
@@ -197,7 +197,6 @@
|
||||
style="width: 62%"
|
||||
v-model="scope.row.adviceName"
|
||||
placeholder="请选择项目"
|
||||
@input="handleChange"
|
||||
@click="handleFocus(scope.row, scope.$index)"
|
||||
@keyup.enter.stop="handleFocus(scope.row, scope.$index)"
|
||||
@keydown="
|
||||
@@ -710,17 +709,9 @@ function loadConfiguredCategories() {
|
||||
// 数据过滤
|
||||
const filterPrescriptionList = computed(() => {
|
||||
const pList = prescriptionList.value.filter((item) => {
|
||||
// 修复 Bug #488:orderClassCode 可能是复合值 '1-2',需提取 adviceType 部分进行比较
|
||||
let matchAdviceType = true;
|
||||
if (orderClassCode.value) {
|
||||
const filterAdviceType = String(orderClassCode.value).includes('-')
|
||||
? parseInt(String(orderClassCode.value).split('-')[0])
|
||||
: orderClassCode.value;
|
||||
matchAdviceType = filterAdviceType == item.adviceType;
|
||||
}
|
||||
return (
|
||||
(!therapyEnum.value || therapyEnum.value == item.therapyEnum) &&
|
||||
matchAdviceType &&
|
||||
(!orderClassCode.value || orderClassCode.value == item.adviceType) &&
|
||||
(!orderStatus.value || (orderStatus.value == item.statusEnum && item.requestId))
|
||||
);
|
||||
});
|
||||
@@ -748,29 +739,13 @@ function getRowDisabled(row) {
|
||||
/**
|
||||
* 将行的 adviceType + categoryCode 映射为 el-select 的选中值
|
||||
* 药品子分类使用复合值如 '1-2'(adviceType-categoryCode),诊疗/手术/全部使用原始值
|
||||
* 修复 Bug #488:当行的 adviceType 在当前配置中找不到匹配选项时,返回最接近的可用值,避免回显为纯数字
|
||||
*/
|
||||
function getRowSelectValue(row) {
|
||||
if (row.adviceType == 1 && row.categoryCode) {
|
||||
const compositeValue = '1-' + row.categoryCode;
|
||||
// 检查复合值是否在选项列表中
|
||||
if (adviceTypeList.value.some(item => item.value === compositeValue)) {
|
||||
return compositeValue;
|
||||
}
|
||||
// 配置的 categoryCode 已变更,回退到第一个药品选项
|
||||
const firstPharmacy = adviceTypeList.value.find(item => String(item.value).startsWith('1-'));
|
||||
if (firstPharmacy) {
|
||||
return firstPharmacy.value;
|
||||
return '1-' + row.categoryCode;
|
||||
}
|
||||
return row.adviceType;
|
||||
}
|
||||
// 诊疗/手术等非药品类型,检查其值是否在选项列表中
|
||||
if (adviceTypeList.value.some(item => item.value === row.adviceType)) {
|
||||
return row.adviceType;
|
||||
}
|
||||
// 不在选项中的值(如已废弃的 adviceType),返回 undefined 让 el-select 显示为空
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// 新增医嘱
|
||||
function handleAddPrescription() {
|
||||
@@ -1241,13 +1216,6 @@ function handleSave() {
|
||||
if (res.code === 200) {
|
||||
proxy.$modal.msgSuccess('签发成功');
|
||||
isSaving.value = false;
|
||||
// 乐观更新:立即将已签发医嘱的状态设为"已签发",确保列表实时刷新
|
||||
saveList.forEach((item) => {
|
||||
const row = prescriptionList.value.find((r) => r.requestId && r.requestId === item.requestId);
|
||||
if (row) {
|
||||
row.statusEnum = 2;
|
||||
}
|
||||
});
|
||||
getListInfo(false);
|
||||
bindMethod.value = {};
|
||||
nextId.value = 1;
|
||||
@@ -1351,12 +1319,11 @@ function handleCancelEdit(row, index) {
|
||||
|
||||
function handleSaveSign(row, index) {
|
||||
if (row.adviceType != 2) {
|
||||
// 修复 Bug #488:严格校验 itemNo,确保非空且为有效字符串才发起请求
|
||||
let itemNo = row.adviceType == 1 ? row.methodCode : row.adviceDefinitionId;
|
||||
if (!itemNo || String(itemNo).trim() === '') {
|
||||
if (!itemNo) {
|
||||
console.warn('绑定设备检查跳过:itemNo为空(adviceType=' + row.adviceType + ', adviceName=' + row.adviceName + ')');
|
||||
} else {
|
||||
getBindDevice({ typeCode: row.adviceType, itemNo: String(itemNo) }).then((res) => {
|
||||
getBindDevice({ typeCode: row.adviceType, itemNo: itemNo }).then((res) => {
|
||||
if (res.data.length == 0) {
|
||||
return;
|
||||
}
|
||||
@@ -1646,14 +1613,20 @@ function handleSaveGroup(orderGroupList) {
|
||||
|
||||
// 创建新的处方项目
|
||||
// 🔧 Bug #403 修复:关键字段使用 null-safe 回退到 mergedDetail(已由 setValue 填充完整数据)
|
||||
// 先取 setValue 填充的行数据作为基础
|
||||
const baseRow = prescriptionList.value[rowIndex.value];
|
||||
const newRow = {
|
||||
...baseRow,
|
||||
...prescriptionList.value[rowIndex.value],
|
||||
patientId: patientInfo.value.patientId,
|
||||
encounterId: patientInfo.value.encounterId,
|
||||
accountId: accountId.value,
|
||||
quantity: item.quantity ?? mergedDetail.quantity,
|
||||
methodCode: item.methodCode ?? mergedDetail.methodCode,
|
||||
rateCode: item.rateCode ?? mergedDetail.rateCode,
|
||||
dispensePerDuration: item.dispensePerDuration ?? mergedDetail.dispensePerDuration,
|
||||
dose: item.dose ?? mergedDetail.dose,
|
||||
doseQuantity: item.doseQuantity ?? mergedDetail.doseQuantity,
|
||||
executeNum: 1,
|
||||
unitCode: item.unitCode ?? mergedDetail.unitCode,
|
||||
unitCode_dictText: item.unitCodeName || mergedDetail.unitCodeName || '',
|
||||
statusEnum: 1,
|
||||
orgId: resolveOrgId(item.orderDetailInfos?.orgId || mergedDetail.orgId || patientInfo.value?.inHospitalOrgId) || '',
|
||||
// 🔧 修复:同时保存 orgName,确保树匹配不到时仍有中文名称可显示
|
||||
@@ -1662,33 +1635,19 @@ function handleSaveGroup(orderGroupList) {
|
||||
conditionId: conditionId.value,
|
||||
conditionDefinitionId: conditionDefinitionId.value,
|
||||
encounterDiagnosisId: encounterDiagnosisId.value,
|
||||
therapyEnum: baseRow?.therapyEnum || '1',
|
||||
therapyEnum: prescriptionList.value[rowIndex.value]?.therapyEnum || '1',
|
||||
};
|
||||
// 覆盖关键字段:优先使用 item 的值,其次 mergedDetail(已由 setValue 填充),最后 baseRow
|
||||
newRow.quantity = item.quantity ?? mergedDetail.quantity ?? baseRow.quantity;
|
||||
newRow.methodCode = item.methodCode ?? mergedDetail.methodCode ?? baseRow.methodCode;
|
||||
newRow.rateCode = item.rateCode ?? mergedDetail.rateCode ?? baseRow.rateCode;
|
||||
newRow.dispensePerDuration = item.dispensePerDuration ?? mergedDetail.dispensePerDuration ?? baseRow.dispensePerDuration;
|
||||
newRow.dose = item.dose ?? mergedDetail.dose ?? baseRow.dose;
|
||||
newRow.doseQuantity = item.doseQuantity ?? mergedDetail.doseQuantity ?? baseRow.doseQuantity;
|
||||
newRow.unitCode = item.unitCode ?? mergedDetail.unitCode ?? baseRow.unitCode;
|
||||
newRow.unitCode_dictText = item.unitCodeName || mergedDetail.unitCodeName || baseRow.unitCode_dictText || '';
|
||||
|
||||
// 计算价格和总量
|
||||
// 🔧 Bug #403 修复:使用 newRow.unitCode(已由 setValue 填充)而非 item.unitCode
|
||||
// 使用 ?? 替代 || 计算 partPercent,确保值为 0 时不会被错误替换
|
||||
const finalUnitCode = newRow.unitCode;
|
||||
const unitInfo = unitCodeList.value.find((k) => k.value == finalUnitCode);
|
||||
const finalQuantity = newRow.quantity;
|
||||
const partPercent = item.orderDetailInfos?.partPercent ?? mergedDetail.partPercent ?? baseRow.partPercent ?? 1;
|
||||
const unitInfo = unitCodeList.value.find((k) => k.value == item.unitCode);
|
||||
if (unitInfo && unitInfo.type == 'minUnit') {
|
||||
newRow.price = newRow.minUnitPrice;
|
||||
newRow.totalPrice = ((finalQuantity || 0) * newRow.minUnitPrice).toFixed(6);
|
||||
newRow.minUnitQuantity = finalQuantity || 0;
|
||||
newRow.totalPrice = (item.quantity * newRow.minUnitPrice).toFixed(6);
|
||||
newRow.minUnitQuantity = item.quantity;
|
||||
} else {
|
||||
newRow.price = newRow.unitPrice;
|
||||
newRow.totalPrice = ((finalQuantity || 0) * newRow.unitPrice).toFixed(6);
|
||||
newRow.minUnitQuantity = (finalQuantity || 0) * partPercent;
|
||||
newRow.totalPrice = (item.quantity * newRow.unitPrice).toFixed(6);
|
||||
newRow.minUnitQuantity = item.quantity * (item.orderDetailInfos?.partPercent || mergedDetail.partPercent || 1);
|
||||
}
|
||||
|
||||
newRow.contentJson = JSON.stringify(newRow);
|
||||
|
||||
@@ -481,43 +481,12 @@ watch(
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
executeTime.value = formatDateStr(new Date(), 'YYYY-MM-DD HH:mm:ss');
|
||||
// 弹窗打开时重新加载科室和位置选项,确保数据最新
|
||||
loadDepartmentOptions();
|
||||
getDiseaseInitLoc(16);
|
||||
} else {
|
||||
resetData();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 监听科室选项加载完成,为已添加的诊疗项目设置默认执行科室
|
||||
watch(
|
||||
() => departmentOptions.value,
|
||||
(depts) => {
|
||||
if (!depts || depts.length === 0) return;
|
||||
feeItemsList.value.forEach(item => {
|
||||
if (item.adviceType === 3 && !item.positionId) {
|
||||
const patientOrgId = props.patientInfo.organizationId;
|
||||
const matched = depts.find(d => String(d.id) === String(patientOrgId));
|
||||
item.positionId = matched ? String(matched.id) : String(depts[0].id);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// 监听位置选项加载完成,为已添加的耗材项目设置默认位置
|
||||
watch(
|
||||
() => locationOptions.value,
|
||||
(locs) => {
|
||||
if (!locs || locs.length === 0) return;
|
||||
feeItemsList.value.forEach(item => {
|
||||
if (item.adviceType === 2 && !item.positionId) {
|
||||
item.positionId = String(locs[0].value);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// 加载科室选项
|
||||
function loadDepartmentOptions() {
|
||||
getOrgList()
|
||||
@@ -558,27 +527,23 @@ function getDiseaseInitLoc() {
|
||||
locationOptions.value = [];
|
||||
});
|
||||
}
|
||||
// 下拉框模糊搜索过滤(自定义filter-method,配合element-plus filterable使用)
|
||||
// 下拉框模糊搜索过滤
|
||||
function filterOptions(val, row, optionsKey) {
|
||||
const key = row.adviceDefinitionId + '_' + optionsKey;
|
||||
if (!val || val.trim() === '') {
|
||||
delete filterKeywords.value[key];
|
||||
} else {
|
||||
filterKeywords.value[key] = val.toLowerCase();
|
||||
}
|
||||
filterKeywords.value[key] = val;
|
||||
}
|
||||
function getFilteredOptions(row, optionsKey) {
|
||||
const key = row.adviceDefinitionId + '_' + optionsKey;
|
||||
const keyword = filterKeywords.value[key];
|
||||
const options = optionsKey === 'departmentOptions' ? departmentOptions.value : locationOptions.value;
|
||||
if (!keyword) {
|
||||
if (!keyword || keyword.trim() === '') {
|
||||
return options;
|
||||
}
|
||||
const lower = keyword.toLowerCase();
|
||||
return options.filter(item => {
|
||||
const name = (item.name || item.label || '').toLowerCase();
|
||||
const id = String(item.id || item.value || '').toLowerCase();
|
||||
const py = (item.pyStr || '').toLowerCase();
|
||||
return name.includes(keyword) || id.includes(keyword) || py.includes(keyword);
|
||||
return name.includes(lower) || id.includes(lower);
|
||||
});
|
||||
}
|
||||
// 获取组套类型文本
|
||||
@@ -588,9 +553,9 @@ function getItemType_Text(type) {
|
||||
}
|
||||
function getUnitCodeOptions(row) {
|
||||
const unitCodes = [
|
||||
{ code: String(row.unitCode), codeText: row.unitCode_dictText },
|
||||
{ code: String(row.minUnitCode), codeText: row.minUnitCode_dictText },
|
||||
].filter(item => item.code);
|
||||
{ code: row.unitCode, codeText: row.unitCode_dictText },
|
||||
{ code: row.minUnitCode, codeText: row.minUnitCode_dictText },
|
||||
];
|
||||
// 使用 Set 来跟踪已经存在的 code
|
||||
const seenCodes = new Set();
|
||||
const uniqueUnitCodes = unitCodes.filter((item) => {
|
||||
@@ -610,11 +575,11 @@ function unitCodeChange(row) {
|
||||
// 获取价格
|
||||
const price = row.priceList?.[0]?.price || 0;
|
||||
|
||||
// 根据选择的单位调整单价(统一用字符串比较)
|
||||
if (String(row.selectUnitCode) === String(row.unitCode)) {
|
||||
// 根据选择的单位调整单价
|
||||
if (row.selectUnitCode === row.unitCode) {
|
||||
// 如果选择的是大单位 (如 "盒")
|
||||
row.unitPrice = price.toFixed(6); // 单价就是原价
|
||||
} else if (String(row.selectUnitCode) === String(row.minUnitCode)) {
|
||||
} else if (row.selectUnitCode === row.minUnitCode) {
|
||||
// 如果选择的是小单位 (如 "个")
|
||||
row.unitPrice = (price / (row.partPercent || 1)).toFixed(6); // 单价 = 原价 / 拆零比
|
||||
}
|
||||
@@ -806,32 +771,15 @@ function openGroupSetDialog() {
|
||||
|
||||
function loadGroupSets() {
|
||||
groupSetLoading.value = true;
|
||||
const params = { organizationId: orgId.value };
|
||||
// 传递搜索关键字,后端 /group-package-for-order 虽不直接支持 searchKey,
|
||||
// 但保持参数传递以便后续扩展
|
||||
if (groupSetSearchText.value && groupSetSearchText.value.trim()) {
|
||||
params.searchKey = groupSetSearchText.value.trim();
|
||||
}
|
||||
getOrderGroup(params)
|
||||
getOrderGroup({ organizationId: orgId.value })
|
||||
.then((res) => {
|
||||
const data = res?.data || {};
|
||||
let rawList = [];
|
||||
if (groupSetRange.value === 1) {
|
||||
rawList = data.personalList || [];
|
||||
groupSetList.value = data.personalList || [];
|
||||
} else if (groupSetRange.value === 2) {
|
||||
rawList = data.organizationList || [];
|
||||
groupSetList.value = data.organizationList || [];
|
||||
} else {
|
||||
rawList = data.hospitalList || [];
|
||||
}
|
||||
// 客户端过滤:根据搜索关键字过滤组套名称
|
||||
const keyword = groupSetSearchText.value?.trim()?.toLowerCase();
|
||||
if (keyword) {
|
||||
groupSetList.value = rawList.filter(item => {
|
||||
const name = (item.name || item.Name || '').toLowerCase();
|
||||
return name.includes(keyword);
|
||||
});
|
||||
} else {
|
||||
groupSetList.value = rawList;
|
||||
groupSetList.value = data.hospitalList || [];
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
@@ -66,21 +66,13 @@ const props = defineProps({
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
therapyEnum: {
|
||||
type: Number,
|
||||
default: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
handleGetPrescription();
|
||||
function handleGetPrescription() {
|
||||
loading.value = true;
|
||||
let encounterIds = patientInfoList.value.map((i) => i.encounterId).join(',');
|
||||
const params = {};
|
||||
if (props.therapyEnum !== undefined) {
|
||||
params.therapyEnum = props.therapyEnum;
|
||||
}
|
||||
getMedicineSummary(params).then((res) => {
|
||||
getMedicineSummary({}).then((res) => {
|
||||
medicineSummaryFormList.value = res.data.records;
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
:deadline="deadline"
|
||||
:therapyEnum="therapyEnum"
|
||||
/>
|
||||
<SummaryMedicineList v-else :therapyEnum="therapyEnum" />
|
||||
<SummaryMedicineList v-else />
|
||||
<!-- <el-tabs v-model="activeName" class="demo-tabs centered-tabs" @tab-change="handleClick">
|
||||
<el-tab-pane
|
||||
v-for="tab in prescriptionTabs"
|
||||
|
||||
@@ -471,13 +471,11 @@ function handleExecute() {
|
||||
console.log(list, 'list');
|
||||
adviceExecute({ exeDate: exeDate.value, adviceExecuteDetailList: list }).then((res) => {
|
||||
if (res.code == 200) {
|
||||
// 仅当选中医嘱中包含诊疗类医嘱(可能绑定耗材)时,才调用耗材批号匹配
|
||||
// adviceTable 取值为 med_medication_request(药品)或 wor_service_request(诊疗/耗材)
|
||||
// 原代码用 includes('device') 判断有误,两个表名均不含 "device" 字符串
|
||||
const hasServiceRequest = list.some((item) =>
|
||||
String(item.adviceTable || '') === 'wor_service_request',
|
||||
// 仅当选中医嘱中包含耗材类医嘱时,才调用耗材批号匹配(排除纯药品医嘱场景)
|
||||
const hasDevice = list.some((item) =>
|
||||
String(item.adviceTable || '').includes('device'),
|
||||
);
|
||||
if (hasServiceRequest) {
|
||||
if (hasDevice) {
|
||||
lotNumberMatch({ encounterIdList: encounterIds }, { skipErrorMsg: true }).catch((error) => {
|
||||
console.warn('lotNumberMatch failed after adviceExecute:', error);
|
||||
});
|
||||
|
||||
@@ -803,7 +803,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<!-- 手术计费弹窗 -->
|
||||
<el-dialog :title="chargeDialogTitle" v-model="showChargeDialog" width="1400px" @close="closeChargeDialog" append-to-body destroy-on-close>
|
||||
<el-dialog :title="chargeDialogTitle" v-model="showChargeDialog" width="1400px" @close="closeChargeDialog" append-to-body>
|
||||
<div style="display: flex; justify-content: space-between; height: 80vh">
|
||||
<div style="width: 100%; border: 1px solid #eee; position: relative">
|
||||
<div style="padding: 10px; border: 1px solid #eee; height: 50px; border-left: 0">
|
||||
@@ -829,7 +829,7 @@
|
||||
</el-descriptions>
|
||||
</div>
|
||||
<div style="padding: 10px">
|
||||
<prescriptionlist :patientInfo="chargePatientInfo" ref="prescriptionRef"
|
||||
<prescriptionlist v-if="showChargeDialog" :patientInfo="chargePatientInfo" ref="prescriptionRef"
|
||||
:generateSourceEnum="1"
|
||||
:sourceBillNo="chargePatientInfo.sourceBillNo" />
|
||||
<div class="overlay" v-if="disabled"></div>
|
||||
@@ -881,13 +881,16 @@ import {
|
||||
deleteSurgerySchedule,
|
||||
getSurgeryScheduleDetail
|
||||
} from '@/api/surgicalschedule'
|
||||
import { listUser } from '@/api/system/user'
|
||||
import { deptTreeSelect } from '@/api/system/user'
|
||||
import { listOperatingRoom } from '@/api/operatingroom'
|
||||
import { getSurgeryPage} from '@/views/inpatientDoctor/home/components/applicationShow/api.js'
|
||||
import { getContract } from '@/views/inpatientDoctor/home/components/api.js'
|
||||
import request from '@/utils/request'
|
||||
import SurgeryCharge from '../charge/surgerycharge/index.vue'
|
||||
import TemporaryMedical from './temporaryMedical.vue'
|
||||
|
||||
// 静默获取字典列表(跳过拦截器错误提示,手术室护士等角色可能无此权限)
|
||||
// 静默获取卫生机构列表(跳过拦截器错误提示,手术室护士等角色可能无此权限)
|
||||
function getTenantPageSilent(query) {
|
||||
return request({
|
||||
url: '/system/tenant/page',
|
||||
@@ -897,36 +900,6 @@ function getTenantPageSilent(query) {
|
||||
})
|
||||
}
|
||||
|
||||
// 静默获取科室树(跳过拦截器错误提示)
|
||||
function deptTreeSelectSilent(params = {}) {
|
||||
return request({
|
||||
url: '/base-data-manage/organization/organization',
|
||||
method: 'get',
|
||||
params: { typeEnum: 2, ...params },
|
||||
skipErrorMsg: true
|
||||
})
|
||||
}
|
||||
|
||||
// 静默获取用户列表(跳过拦截器错误提示)
|
||||
function listUserSilent(query) {
|
||||
return request({
|
||||
url: '/base-data-manage/practitioner/user-practitioner-page',
|
||||
method: 'get',
|
||||
params: query,
|
||||
skipErrorMsg: true
|
||||
})
|
||||
}
|
||||
|
||||
// 静默获取手术室列表(跳过拦截器错误提示)
|
||||
function listOperatingRoomSilent(query) {
|
||||
return request({
|
||||
url: '/base-data-manage/operating-room/list',
|
||||
method: 'get',
|
||||
params: query,
|
||||
skipErrorMsg: true
|
||||
})
|
||||
}
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
const userStore = useUserStore()
|
||||
const loading = ref(true)
|
||||
@@ -1175,7 +1148,7 @@ function loadOrgList() {
|
||||
|
||||
// 加载科室列表
|
||||
function loadDeptList() {
|
||||
deptTreeSelectSilent()
|
||||
deptTreeSelect()
|
||||
.then(res => {
|
||||
if (res.code === 200) {
|
||||
const tree = res.data?.records || res.data || []
|
||||
@@ -1195,7 +1168,7 @@ function loadDeptList() {
|
||||
|
||||
// 加载医生列表
|
||||
function loadDoctorList() {
|
||||
listUserSilent({ pageNo: 1, pageSize: 1000 })
|
||||
listUser({ pageNo: 1, pageSize: 1000 })
|
||||
.then(res => {
|
||||
if (res.code === 200) {
|
||||
const records = res.data?.records || []
|
||||
@@ -1215,7 +1188,7 @@ function loadDoctorList() {
|
||||
|
||||
// 加载护士列表
|
||||
function loadNurseList() {
|
||||
listUserSilent({ pageNo: 1, pageSize: 1000 })
|
||||
listUser({ pageNo: 1, pageSize: 1000 })
|
||||
.then(res => {
|
||||
if (res.code === 200) {
|
||||
const records = res.data?.records || []
|
||||
@@ -1235,7 +1208,7 @@ function loadNurseList() {
|
||||
|
||||
// 加载手术室列表
|
||||
function loadOperatingRoomList() {
|
||||
listOperatingRoomSilent({ pageNo: 1, pageSize: 1000, statusEnum: 1 })
|
||||
listOperatingRoom({ pageNo: 1, pageSize: 1000, statusEnum: 1 })
|
||||
.then(res => {
|
||||
if (res.code === 200) {
|
||||
const records = res.data?.records || []
|
||||
@@ -1456,18 +1429,17 @@ async function handleChargeCharge(row) {
|
||||
}
|
||||
|
||||
// 关闭计费弹窗
|
||||
async function closeChargeDialog() {
|
||||
function closeChargeDialog() {
|
||||
// 先关闭 prescriptionlist 内所有已打开的项目字典 popover
|
||||
if (prescriptionRef.value && prescriptionRef.value.closeAllPopovers) {
|
||||
prescriptionRef.value.closeAllPopovers()
|
||||
}
|
||||
// 等待 Vue 完成 popover 可见性更新的 DOM 操作,
|
||||
// 因为 el-popover 通过 teleport 渲染在 body 上,需要在 dialog 卸载前完成清理
|
||||
await nextTick()
|
||||
// 清空数据,避免下次打开时使用缓存
|
||||
// 等 Vue 完成 DOM 更新后再关闭弹窗,确保 popover 先消失
|
||||
nextTick(() => {
|
||||
showChargeDialog.value = false
|
||||
chargePatientInfo.value = {}
|
||||
chargeSurgeryInfo.value = {}
|
||||
})
|
||||
}
|
||||
|
||||
// 🔧 新增:标志位,用于区分是"打开"还是"刷新"
|
||||
@@ -1543,8 +1515,6 @@ function handleMedicalAdvice(row) {
|
||||
const filteredItems = res.data.filter(item => {
|
||||
// 匹配 encounterId
|
||||
if (item.encounterId !== row.visitId) return false;
|
||||
// 只保留药品类型(adviceType=1),过滤掉耗材(2)和诊疗项目(3)
|
||||
if (item.adviceType !== 1) return false;
|
||||
// 过滤掉名称为空的项目
|
||||
const medicineName = item.adviceName || item.advice_name;
|
||||
if (!medicineName || medicineName.trim() === '') return false;
|
||||
@@ -1803,12 +1773,10 @@ function handleQuoteBilling() {
|
||||
temporaryBillingMedicines.value = []
|
||||
temporaryAdvices.value = []
|
||||
|
||||
// 只保留药品类型(adviceType=1),过滤掉耗材(2)和诊疗项目(3)
|
||||
// 🔧 修复:显示所有药品请求数据,不管有没有计费项目
|
||||
const filteredItems = res.data.filter(item => {
|
||||
// 匹配 encounterId
|
||||
if (item.encounterId !== temporaryPatientInfo.value.visitId) return false;
|
||||
// 只保留药品类型(adviceType=1),过滤掉耗材(2)和诊疗项目(3)
|
||||
if (item.adviceType !== 1) return false;
|
||||
// 过滤掉名称为空的项目
|
||||
const medicineName = item.adviceName || item.advice_name;
|
||||
return medicineName && medicineName.trim() !== '';
|
||||
|
||||
@@ -312,13 +312,6 @@ const getMethodCodeDict = computed(() => {
|
||||
|
||||
// 响应式数据 - isSigned 从父组件传入的 prop 初始化
|
||||
const isSigned = ref(props.isSignedProp)
|
||||
|
||||
// 🔧 修复 Bug #446: 同步父组件 isSignedProp 的变化到本地 isSigned
|
||||
// ref(props.isSignedProp) 只在初始化时读取一次,父组件后续更新不会自动同步
|
||||
watch(() => props.isSignedProp, (newVal) => {
|
||||
isSigned.value = newVal
|
||||
})
|
||||
|
||||
const signatureTime = ref('')
|
||||
const showSignDialog = ref(false)
|
||||
const signPassword = ref('')
|
||||
|
||||
Reference in New Issue
Block a user