bug362 413 498 504 507

This commit is contained in:
Ranyunqiao
2026-05-14 11:47:18 +08:00
parent ab2f580d60
commit eab0119c19
19 changed files with 1073 additions and 141 deletions

View File

@@ -366,7 +366,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
serviceRequest.setTherapyEnum(TherapyTimeType.TEMPORARY.getValue());// 治疗类型 serviceRequest.setTherapyEnum(TherapyTimeType.TEMPORARY.getValue());// 治疗类型
serviceRequest.setQuantity(BigDecimal.valueOf(1)); // 请求数量 serviceRequest.setQuantity(BigDecimal.valueOf(1)); // 请求数量
serviceRequest.setUnitCode(""); // 请求单位编码 serviceRequest.setUnitCode(""); // 请求单位编码
serviceRequest.setCategoryEnum(4); // 请求类型4-手术 serviceRequest.setCategoryEnum(24); // 请求类型:24-手术(新值域,避开 adviceType 碰撞)
serviceRequest.setActivityId(surgeryId); // 手术ID作为诊疗定义id serviceRequest.setActivityId(surgeryId); // 手术ID作为诊疗定义id
serviceRequest.setPatientId(surgeryDto.getPatientId()); // 患者 serviceRequest.setPatientId(surgeryDto.getPatientId()); // 患者
serviceRequest.setRequesterId(practitionerId); // 开方医生 serviceRequest.setRequesterId(practitionerId); // 开方医生

View File

@@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import com.openhis.surgicalschedule.domain.OpSchedule; import com.openhis.surgicalschedule.domain.OpSchedule;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate; import java.time.LocalDate;
@@ -16,6 +17,7 @@ import java.time.LocalDate;
* @date 2026-01-28 * @date 2026-01-28
*/ */
@Data @Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class OpScheduleDto extends OpSchedule { public class OpScheduleDto extends OpSchedule {

View File

@@ -1534,7 +1534,18 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
deviceRequest.setLotNumber(adviceSaveDto.getLotNumber());// 产品批号 deviceRequest.setLotNumber(adviceSaveDto.getLotNumber());// 产品批号
deviceRequest.setCategoryEnum(adviceSaveDto.getCategoryEnum()); // 请求类型 deviceRequest.setCategoryEnum(adviceSaveDto.getCategoryEnum()); // 请求类型
// 🔧 BugFix #498: categoryEnum=22(检查) 走 ServiceRequest不走 DeviceRequest
// 检查申请单的诊疗定义ID存在 activityId不在 adviceDefinitionId
// deviceDefId 对应耗材定义ID不能用诊疗定义ID填充
if (adviceSaveDto.getCategoryEnum() == 22) {
log.info("handDevice skip - 检查申请单(categoryEnum=22) 走 ServiceRequest 路径,跳过 DeviceRequest 保存");
continue; // 跳过本次循环,不走耗材请求路径
} else if (adviceSaveDto.getAdviceDefinitionId() != null) {
deviceRequest.setDeviceDefId(adviceSaveDto.getAdviceDefinitionId());// 耗材定义id deviceRequest.setDeviceDefId(adviceSaveDto.getAdviceDefinitionId());// 耗材定义id
} else {
log.warn("handDevice - deviceDefId 为空adviceDefinitionId=null, categoryEnum={}", adviceSaveDto.getCategoryEnum());
}
deviceRequest.setPatientId(adviceSaveDto.getPatientId()); // 患者 deviceRequest.setPatientId(adviceSaveDto.getPatientId()); // 患者
deviceRequest.setRequesterId(adviceSaveDto.getPractitionerId()); // 开方医生 deviceRequest.setRequesterId(adviceSaveDto.getPractitionerId()); // 开方医生
deviceRequest.setOrgId(adviceSaveDto.getFounderOrgId());// 开方人科室 deviceRequest.setOrgId(adviceSaveDto.getFounderOrgId());// 开方人科室

View File

@@ -63,4 +63,20 @@ 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

@@ -761,6 +761,8 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
if (is_sign) { if (is_sign) {
deviceRequest.setReqAuthoredTime(authoredTime); // 医嘱签发时间 deviceRequest.setReqAuthoredTime(authoredTime); // 医嘱签发时间
} }
// 保存或签发时都需要设置耗材定义ID防止 sign 分支 deviceDefId 为空触发 NOT NULL 约束)
deviceRequest.setDeviceDefId(regAdviceSaveDto.getAdviceDefinitionId());
// 保存时处理的字段属性 // 保存时处理的字段属性
if (is_save) { if (is_save) {
deviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.DEVICE_RES_NO.getPrefix(), 4)); deviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.DEVICE_RES_NO.getPrefix(), 4));
@@ -798,6 +800,8 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
if (is_sign) { if (is_sign) {
deviceRequest.setReqAuthoredTime(authoredTime); // 医嘱签发时间 deviceRequest.setReqAuthoredTime(authoredTime); // 医嘱签发时间
} }
// 保存或签发时都需要设置耗材定义ID防止 sign 分支 deviceDefId 为空触发 NOT NULL 约束)
deviceRequest.setDeviceDefId(regAdviceSaveDto.getAdviceDefinitionId());
// 保存时处理的字段属性 // 保存时处理的字段属性
if (is_save) { if (is_save) {
deviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.DEVICE_RES_NO.getPrefix(), 4)); deviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.DEVICE_RES_NO.getPrefix(), 4));

View File

@@ -1,6 +1,7 @@
package com.openhis.web.regdoctorstation.appservice.impl; package com.openhis.web.regdoctorstation.appservice.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R; import com.core.common.core.domain.R;
@@ -294,7 +295,7 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
surgeryServiceRequest.setTherapyEnum(TherapyTimeType.TEMPORARY.getValue()); surgeryServiceRequest.setTherapyEnum(TherapyTimeType.TEMPORARY.getValue());
surgeryServiceRequest.setQuantity(BigDecimal.valueOf(1)); surgeryServiceRequest.setQuantity(BigDecimal.valueOf(1));
surgeryServiceRequest.setUnitCode(""); surgeryServiceRequest.setUnitCode("");
surgeryServiceRequest.setCategoryEnum(4); // 4-手术 surgeryServiceRequest.setCategoryEnum(24); // 24-手术(新值域,避开 adviceType 碰撞)
// 优先从 activityList 获取手术 ID // 优先从 activityList 获取手术 ID
if (activityList != null && !activityList.isEmpty()) { if (activityList != null && !activityList.isEmpty()) {
Long activityId = activityList.get(0).getAdviceDefinitionId(); Long activityId = activityList.get(0).getAdviceDefinitionId();
@@ -490,4 +491,90 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
return requestFormManageAppMapper.getRequestFormPage(requestFormDto, page); return requestFormManageAppMapper.getRequestFormPage(requestFormDto, page);
} }
@Override
public R<?> deleteRequestForm(Long requestFormId) {
if (requestFormId == null) {
return R.fail("申请单ID不能为空");
}
RequestForm requestForm = iRequestFormService.getById(requestFormId);
if (requestForm == null) {
return R.fail("申请单不存在");
}
String prescriptionNo = requestForm.getPrescriptionNo();
// 查询该申请单下所有 ServiceRequest含子项
List<ServiceRequest> serviceRequests = iServiceRequestService.list(
new LambdaQueryWrapper<ServiceRequest>()
.eq(ServiceRequest::getPrescriptionNo, prescriptionNo));
if (serviceRequests == null || serviceRequests.isEmpty()) {
return R.fail("未找到关联的诊疗医嘱");
}
// 校验:只有待签发(status=0)的申请单可删除
boolean allDraft = serviceRequests.stream()
.allMatch(sr -> RequestStatus.DRAFT.getValue().equals(sr.getStatusEnum()));
if (!allDraft) {
return R.fail("只有待签发状态的申请单可删除");
}
List<Long> serviceRequestIds = serviceRequests.stream()
.map(ServiceRequest::getId).collect(Collectors.toList());
// 1. 删除关联的费用项
for (Long srId : serviceRequestIds) {
iChargeItemService.deleteByServiceTableAndId(
CommonConstants.TableName.WOR_SERVICE_REQUEST, srId);
}
// 2. 删除子项 ServiceRequestparentId 非空)
iServiceRequestService.remove(
new LambdaQueryWrapper<ServiceRequest>()
.in(ServiceRequest::getId, serviceRequestIds)
.isNotNull(ServiceRequest::getParentId));
// 3. 删除主项 ServiceRequest
iServiceRequestService.removeByIds(serviceRequestIds);
// 4. 删除申请单
iRequestFormService.removeById(requestFormId);
log.info("检查申请单删除成功requestFormId={}, prescriptionNo={}", requestFormId, prescriptionNo);
return R.ok("删除成功");
}
@Override
public R<?> withdrawRequestForm(Long requestFormId) {
if (requestFormId == null) {
return R.fail("申请单ID不能为空");
}
RequestForm requestForm = iRequestFormService.getById(requestFormId);
if (requestForm == null) {
return R.fail("申请单不存在");
}
String prescriptionNo = requestForm.getPrescriptionNo();
// 查询该申请单下所有 ServiceRequest
List<ServiceRequest> serviceRequests = iServiceRequestService.list(
new LambdaQueryWrapper<ServiceRequest>()
.eq(ServiceRequest::getPrescriptionNo, prescriptionNo));
if (serviceRequests == null || serviceRequests.isEmpty()) {
return R.fail("未找到关联的诊疗医嘱");
}
// 校验:只有已签发(status=2)的申请单可撤回
boolean allActive = serviceRequests.stream()
.allMatch(sr -> RequestStatus.ACTIVE.getValue().equals(sr.getStatusEnum()));
if (!allActive) {
return R.fail("只有已签发状态的申请单可撤回");
}
// 将所有 ServiceRequest 状态改回待签发(DRAFT=0)
List<Long> serviceRequestIds = serviceRequests.stream()
.map(ServiceRequest::getId).collect(Collectors.toList());
iServiceRequestService.update(
new ServiceRequest().setStatusEnum(RequestStatus.DRAFT.getValue()),
new LambdaUpdateWrapper<ServiceRequest>()
.in(ServiceRequest::getId, serviceRequestIds));
log.info("检查申请单撤回成功requestFormId={}, prescriptionNo={}", requestFormId, prescriptionNo);
return R.ok("撤回成功");
}
} }

View File

@@ -18,6 +18,7 @@ import org.springframework.web.bind.annotation.*;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 申请单管理 controller * 申请单管理 controller
@@ -185,4 +186,26 @@ public class RequestFormManageController {
public R<IPage<RequestFormPageDto>> getRequestFormPage(@RequestBody RequestFormDto requestFormDto) { public R<IPage<RequestFormPageDto>> getRequestFormPage(@RequestBody RequestFormDto requestFormDto) {
return R.ok(iRequestFormManageAppService.getRequestFormPage(requestFormDto)); return R.ok(iRequestFormManageAppService.getRequestFormPage(requestFormDto));
} }
/**
* 删除申请单(仅待签发状态可删除)
*
* @param data 包含 requestFormId 的请求体
* @return 结果
*/
@PostMapping(value = "/delete")
public R<?> deleteRequestForm(@RequestBody Map<String, Long> data) {
return iRequestFormManageAppService.deleteRequestForm(data.get("requestFormId"));
}
/**
* 撤回申请单(已签发状态撤回至待签发)
*
* @param data 包含 requestFormId 的请求体
* @return 结果
*/
@PostMapping(value = "/withdraw")
public R<?> withdrawRequestForm(@RequestBody Map<String, Long> data) {
return iRequestFormManageAppService.withdrawRequestForm(data.get("requestFormId"));
}
} }

View File

@@ -264,7 +264,6 @@
WHERE ael.status_enum = #{active} WHERE ael.status_enum = #{active}
AND ael.delete_flag = '0' AND ael.delete_flag = '0'
AND ael.form_enum = #{bed} AND ael.form_enum = #{bed}
LIMIT 1
) AS bed ON bed.encounter_id = ae.id ) AS bed ON bed.encounter_id = ae.id
LEFT JOIN ( LEFT JOIN (
SELECT ael.encounter_id, SELECT ael.encounter_id,
@@ -275,7 +274,6 @@
WHERE ael.status_enum = #{active} WHERE ael.status_enum = #{active}
AND ael.delete_flag = '0' AND ael.delete_flag = '0'
AND ael.form_enum = #{house} AND ael.form_enum = #{house}
LIMIT 1
) AS house ON house.encounter_id = ae.id ) AS house ON house.encounter_id = ae.id
LEFT JOIN ( LEFT JOIN (
SELECT ael.encounter_id, SELECT ael.encounter_id,
@@ -286,7 +284,6 @@
WHERE ael.status_enum = #{active} WHERE ael.status_enum = #{active}
AND ael.delete_flag = '0' AND ael.delete_flag = '0'
AND ael.form_enum = #{ward} AND ael.form_enum = #{ward}
LIMIT 1
) AS ward ON ward.encounter_id = ae.id ) AS ward ON ward.encounter_id = ae.id
LEFT JOIN ( LEFT JOIN (
SELECT aep.encounter_id, SELECT aep.encounter_id,

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 3 END AS advice_type, (SELECT CASE WHEN T1.category_enum IN (4, 24) 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,

View File

@@ -13,7 +13,17 @@
drf.requester_id, drf.requester_id,
drf.create_time, drf.create_time,
ap.NAME AS patient_name, ap.NAME AS patient_name,
drf.status CASE MIN(wsr.status_enum)
WHEN 1 THEN 0
WHEN 2 THEN 1
WHEN 3 THEN 4
WHEN 4 THEN 4
WHEN 5 THEN 5
WHEN 6 THEN 5
WHEN 7 THEN 5
WHEN 8 THEN 6
ELSE NULL
END AS status
FROM doc_request_form AS drf FROM doc_request_form AS drf
LEFT JOIN adm_encounter AS ae ON ae.ID = drf.encounter_id LEFT JOIN adm_encounter AS ae ON ae.ID = drf.encounter_id
AND ae.delete_flag = '0' AND ae.delete_flag = '0'
@@ -31,7 +41,17 @@
AND drf.create_time &lt;= (#{endDate}::date + INTERVAL '1 day' - INTERVAL '1 second') AND drf.create_time &lt;= (#{endDate}::date + INTERVAL '1 day' - INTERVAL '1 second')
</if> </if>
<if test="status != null and status != ''"> <if test="status != null and status != ''">
AND drf.status = #{status}::integer AND CASE MIN(wsr.status_enum)
WHEN 1 THEN 0
WHEN 2 THEN 1
WHEN 3 THEN 4
WHEN 4 THEN 4
WHEN 5 THEN 5
WHEN 6 THEN 5
WHEN 7 THEN 5
WHEN 8 THEN 6
ELSE NULL
END = #{status}::integer
</if> </if>
<if test="keyword != null and keyword != ''"> <if test="keyword != null and keyword != ''">
AND (drf.prescription_no ILIKE '%' || #{keyword} || '%' AND (drf.prescription_no ILIKE '%' || #{keyword} || '%'

View File

@@ -49,6 +49,40 @@
</el-col> </el-col>
</el-row> </el-row>
<!-- 性别出生日期或实足年龄 -->
<el-row :gutter="16" class="form-row">
<el-col :span="7" class="form-item">
<span class="form-label required">性别</span>
<el-radio-group v-model="form.sex" class="gender-radio-group">
<el-radio value="男"></el-radio>
<el-radio value="女"></el-radio>
<el-radio value="未知">未知</el-radio>
</el-radio-group>
</el-col>
<el-col :span="10" class="form-item">
<span class="form-label required">出生日期</span>
<div class="birth-input-group">
<el-input v-model="form.birthYear" class="birth-input year" placeholder="年" maxlength="4" />
<span class="birth-separator"></span>
<el-input v-model="form.birthMonth" class="birth-input month" placeholder="月" maxlength="2" />
<span class="birth-separator"></span>
<el-input v-model="form.birthDay" class="birth-input day" placeholder="日" maxlength="2" />
<span class="birth-separator"></span>
</div>
</el-col>
<el-col :span="7" class="form-item">
<span class="form-label"> 实足年龄</span>
<div class="age-input-group">
<el-input v-model="form.age" class="age-input" placeholder="年龄" />
<el-select v-model="form.ageUnit" class="age-unit-select">
<el-option label="岁" value="岁" />
<el-option label="月" value="月" />
<el-option label="天" value="天" />
</el-select>
</div>
</el-col>
</el-row>
<!-- 联系电话紧急联系人电话 --> <!-- 联系电话紧急联系人电话 -->
<el-row :gutter="16" class="form-row"> <el-row :gutter="16" class="form-row">
<el-col :span="12" class="form-item"> <el-col :span="12" class="form-item">
@@ -1992,4 +2026,53 @@ defineExpose({ show, showReport, close: handleClose });
display: flex !important; display: flex !important;
justify-content: center !important; justify-content: center !important;
} }
/* 性别单选按钮组 */
.gender-radio-group {
display: flex;
gap: 12px;
padding-top: 4px;
}
/* 出生日期输入组 */
.birth-input-group {
display: flex;
align-items: center;
gap: 2px;
}
.birth-input {
text-align: center;
}
.birth-input.year {
width: 70px;
}
.birth-input.month,
.birth-input.day {
width: 50px;
}
.birth-separator {
color: #606266;
font-size: 13px;
margin: 0 2px;
}
/* 年龄输入组 */
.age-input-group {
display: flex;
align-items: center;
gap: 6px;
}
.age-input {
width: 70px;
text-align: center;
}
.age-unit-select {
width: 65px;
}
</style> </style>

View File

@@ -92,45 +92,42 @@
<el-table-column prop="requesterId_dictText" label="申请者" width="120" /> <el-table-column prop="requesterId_dictText" label="申请者" width="120" />
<el-table-column label="申请单状态" width="120" align="center"> <el-table-column label="申请单状态" width="120" align="center">
<template #default="scope"> <template #default="scope">
<span>{{ parseStatus(scope.row.status) }}</span> <el-tag :type="getStatusTagType(scope.row.status)" effect="plain" round>
{{ parseStatus(scope.row.status) }}
</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="280" align="center" fixed="right"> <el-table-column label="操作" width="280" align="center" fixed="right">
<template #default="scope"> <template #default="scope">
<!-- 待签发详情修改删除 --> <!-- 详情 - 所有状态都显示 -->
<template v-if="scope.row.status === '0' || scope.row.status === 0">
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button> <el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
<el-button link type="primary" @click="handleModify(scope.row)">修改</el-button> <!-- 待签发修改删除 -->
<template v-if="scope.row.status === '0' || scope.row.status === 0">
<el-button link type="primary" @click="handleEdit(scope.row)">修改</el-button>
<el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button> <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template> </template>
<!-- 已签发详情撤回 --> <!-- 已签发撤回 -->
<template v-else-if="scope.row.status === '1' || scope.row.status === 1"> <template v-else-if="scope.row.status === '1' || scope.row.status === 1">
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button> <el-button link type="warning" @click="handleRecall(scope.row)">撤回</el-button>
<el-button link type="warning" @click="handleWithdraw(scope.row)">撤回</el-button>
</template> </template>
<!-- 已校对/待接收详情打印 --> <!-- 已校对/待接收打印 -->
<template v-else-if="scope.row.status === '2' || scope.row.status === 2 || scope.row.status === '3' || scope.row.status === 3"> <template v-else-if="scope.row.status === '2' || scope.row.status === 2 || scope.row.status === '3' || scope.row.status === 3">
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
<el-button link type="primary" @click="handlePrint(scope.row)">打印</el-button> <el-button link type="primary" @click="handlePrint(scope.row)">打印</el-button>
</template> </template>
<!-- 已接收/已检查详情看报告 --> <!-- 已接收/已检查看报告 -->
<template v-else-if="scope.row.status === '4' || scope.row.status === 4 || scope.row.status === '5' || scope.row.status === 5"> <template v-else-if="scope.row.status === '4' || scope.row.status === 4 || scope.row.status === '5' || scope.row.status === 5">
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
<el-button link type="success" @click="handleViewReport(scope.row)">看报告</el-button> <el-button link type="success" @click="handleViewReport(scope.row)">看报告</el-button>
</template> </template>
<!-- 已出报告详情打印看报告 --> <!-- 已出报告打印看报告 -->
<template v-else-if="scope.row.status === '6' || scope.row.status === 6"> <template v-else-if="scope.row.status === '6' || scope.row.status === 6">
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
<el-button link type="primary" @click="handlePrint(scope.row)">打印</el-button> <el-button link type="primary" @click="handlePrint(scope.row)">打印</el-button>
<el-button link type="success" @click="handleViewReport(scope.row)">看报告</el-button> <el-button link type="success" @click="handleViewReport(scope.row)">看报告</el-button>
</template> </template>
<!-- 已作废详情 --> <!-- 已作废无额外按钮 -->
<template v-else-if="scope.row.status === '7' || scope.row.status === 7"> <template v-else-if="scope.row.status === '7' || scope.row.status === 7">
<el-button link type="info" @click="handleViewDetail(scope.row)">详情</el-button>
</template> </template>
<!-- 其他/未知状态仅详情 --> <!-- 其他/未知状态仅详情 -->
<template v-else> <template v-else>
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
</template> </template>
</template> </template>
</el-table-column> </el-table-column>
@@ -203,15 +200,102 @@
<el-button @click="detailDialogVisible = false">关闭</el-button> <el-button @click="detailDialogVisible = false">关闭</el-button>
</template> </template>
</el-dialog> </el-dialog>
<!-- 修改申请单弹窗 - 复用检查申请单组件 -->
<el-dialog
v-model="editDialogVisible"
title="修改检查申请"
width="1200px"
destroy-on-close
top="5vh"
:close-on-click-modal="false"
>
<MedicalExaminations
v-if="editDialogVisible"
ref="editFormRef"
:is-edit-mode="true"
:edit-data="editingRow"
:external-patient-info="patientInfo"
/>
<template #footer>
<el-button @click="editDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleEditSubmit">确认</el-button>
</template>
</el-dialog>
<!-- 查看报告弹窗 -->
<el-dialog
v-model="reportDialogVisible"
:title="'检查报告 - ' + (reportData?.prescriptionNo || '')"
width="900px"
destroy-on-close
top="5vh"
:close-on-click-modal="false"
>
<div v-loading="reportLoading" class="report-viewer">
<!-- 报告基本信息 -->
<div v-if="reportData" class="report-viewer-container">
<el-descriptions title="报告信息" :column="2" border size="small">
<el-descriptions-item label="患者姓名">{{ reportData.patientName || reportRow?.patientName || '-' }}</el-descriptions-item>
<el-descriptions-item label="申请单号">{{ reportData.prescriptionNo || reportRow?.prescriptionNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="申请单名称">{{ reportData.name || reportRow?.name || '-' }}</el-descriptions-item>
<el-descriptions-item label="报告时间">{{ reportData.reportTime || '-' }}</el-descriptions-item>
<el-descriptions-item label="诊断意见" :span="2">{{ reportData.diagnosis || reportData.conclusion || '-' }}</el-descriptions-item>
</el-descriptions>
<!-- 报告详细内容 -->
<div v-if="reportData.content" class="report-content-section">
<div class="section-title">报告内容</div>
<div class="report-content-text">{{ reportData.content }}</div>
</div>
<!-- 影像预览 - PACS链接 -->
<div v-if="reportData.imageUrl || reportData.pacsUrl" class="report-image-section">
<div class="section-title">
影像预览
<el-button
v-if="reportData.imageUrl || reportData.pacsUrl"
type="primary"
size="small"
plain
style="margin-left: 12px;"
@click="openPacsLink"
>
<el-icon><Link /></el-icon>
打开PACS影像
</el-button>
</div>
<iframe
v-if="reportData.imageUrl"
:src="reportData.imageUrl"
class="report-iframe"
frameborder="0"
/>
<el-empty v-else-if="!reportData.imageUrl && reportData.pacsUrl" description="点击上方按钮打开PACS影像" :image-size="60" />
</div>
<!-- 完全无数据时的提示 -->
<el-empty v-if="!reportData.content && !reportData.imageUrl && !reportData.pacsUrl" description="暂无详细报告数据" :image-size="60" />
</div>
<!-- 未获取到报告 -->
<el-empty v-else description="暂未生成报告" :image-size="80" />
</div>
<template #footer>
<el-button @click="reportDialogVisible = false">关闭</el-button>
</template>
</el-dialog>
</div> </div>
</template> </template>
<script setup> <script setup>
import {computed, getCurrentInstance, ref, watch} from 'vue'; import {computed, getCurrentInstance, ref, watch, nextTick} from 'vue';
import {Refresh, Search} from '@element-plus/icons-vue'; import {Refresh, Search, Link} from '@element-plus/icons-vue';
import {patientInfo} from '../../store/patient.js'; import {patientInfo} from '../../store/patient.js';
import {getCheck, deleteRequestForm, withdrawRequestForm, getTestResult} from './api'; import {getCheck, deleteRequestForm, withdrawRequestForm, getTestResult} from './api';
import {getDepartmentList} from '@/api/public.js'; import {getDepartmentList} from '@/api/public.js';
import {getApplicationList, saveCheckd} from '../order/applicationForm/api';
import MedicalExaminations from '../order/applicationForm/medicalExaminations.vue';
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
@@ -221,6 +305,24 @@ const detailDialogVisible = ref(false);
const currentDetail = ref(null); const currentDetail = ref(null);
const descJsonData = ref(null); const descJsonData = ref(null);
const orgOptions = ref([]); const orgOptions = ref([]);
const editForm = ref({
name: '',
targetDepartment: '',
symptom: '',
sign: '',
clinicalDiagnosis: '',
otherDiagnosis: '',
relatedResult: '',
attention: '',
examinationPurpose: '',
medicalHistorySummary: '',
});
// 报告弹窗相关
const reportDialogVisible = ref(false);
const reportLoading = ref(false);
const reportData = ref(null);
const reportRow = ref(null);
// 获取近7天的日期范围作为默认值 // 获取近7天的日期范围作为默认值
const getDefaultDateRange = () => { const getDefaultDateRange = () => {
@@ -268,7 +370,9 @@ const fetchData = async () => {
if (res.code === 200 && res.data) { if (res.code === 200 && res.data) {
const raw = res.data?.records || res.data; const raw = res.data?.records || res.data;
const list = Array.isArray(raw) ? raw : [raw]; const list = Array.isArray(raw) ? raw : [raw];
console.log('API返回的原始数据:', JSON.stringify(list, null, 2));
tableData.value = list.filter(Boolean); tableData.value = list.filter(Boolean);
console.log('tableData设置后的第一条:', tableData.value[0]);
} else { } else {
tableData.value = []; tableData.value = [];
} }
@@ -325,6 +429,25 @@ const parseStatus = (status) => {
return statusMap[String(status)] || '-'; return statusMap[String(status)] || '-';
}; };
/**
* 获取状态标签类型 - 参考临床医嘱样式
* @param {string|number} status - 状态码
* @returns {string} el-tag type
*/
const getStatusTagType = (status) => {
const typeMap = {
'0': 'primary', // 待签发 - 蓝色
'1': 'success', // 已签发 - 绿色
'2': 'success', // 已校对 - 绿色
'3': 'primary', // 待接收 - 蓝色
'4': 'primary', // 已接收 - 蓝色
'5': 'success', // 已检查 - 绿色
'6': 'success', // 已出报告 - 绿色
'7': 'danger', // 已作废 - 红色
};
return typeMap[String(status)] || 'info';
};
const labelMap = { const labelMap = {
categoryType: '项目类别', categoryType: '项目类别',
targetDepartment: '发往科室', targetDepartment: '发往科室',
@@ -418,87 +541,391 @@ const handleViewDetail = async (row) => {
}; };
/** /**
* 修改申请单仅待签发状态 * 删除申请单 - 仅待签发状态可用
*/ */
const handleModify = (row) => { const handleDelete = async (row) => {
proxy.$modal?.msgWarning?.('修改功能需后端支持,请联系管理员'); try {
}; await proxy.$modal?.confirm?.('确认删除该检查申请单?', '提示', {
/**
* 删除申请单(仅待签发状态)
*/
const handleDelete = (row) => {
proxy.$confirm?.('确认删除该检查申请单吗?删除后不可恢复。', '警告', {
confirmButtonText: '确定', confirmButtonText: '确定',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning', type: 'warning',
}).then(async () => { });
try { const res = await deleteRequestForm({ requestFormId: row.requestFormId });
const res = await deleteRequestForm({ requestFormId: row.requestFormId || row.id }); if (res.code === 200) {
if (res?.code === 200) {
proxy.$modal?.msgSuccess?.('删除成功'); proxy.$modal?.msgSuccess?.('删除成功');
await fetchData(); fetchData();
} else { } else {
proxy.$modal?.msgError?.(res?.msg || '删除失败'); proxy.$modal?.msgError?.(res.msg || '删除失败');
} }
} catch (e) { } catch (e) {
console.warn('删除申请单失败(可能后端未实现):', e.message); // 用户取消操作,不做处理
proxy.$modal?.msgError?.('删除失败,后端服务可能未支持此功能');
} }
}).catch(() => {});
}; };
/** /**
* 撤回申请单已签发状态撤回至待签发) * 撤回申请单 - 仅已签发状态可用
*/ */
const handleWithdraw = (row) => { const handleRecall = async (row) => {
proxy.$confirm?.('确认撤回该检查申请单吗?撤回后状态将变为待签发。', '撤回确认', { try {
confirmButtonText: '确定', await proxy.$modal?.confirm?.('确认撤回该申请单?撤回后状态将变更为"待签发"。', '提示', {
confirmButtonText: '确定撤回',
cancelButtonText: '取消', cancelButtonText: '取消',
type: 'warning', type: 'warning',
}).then(async () => { });
try { const res = await withdrawRequestForm({ requestFormId: row.requestFormId });
const res = await withdrawRequestForm({ requestFormId: row.requestFormId || row.id }); if (res.code === 200) {
if (res?.code === 200) {
proxy.$modal?.msgSuccess?.('撤回成功'); proxy.$modal?.msgSuccess?.('撤回成功');
await fetchData(); fetchData();
} else { } else {
proxy.$modal?.msgError?.(res?.msg || '撤回失败'); proxy.$modal?.msgError?.(res.msg || '撤回失败');
} }
} catch (e) { } catch (e) {
console.warn('撤回申请单失败(可能后端未实现):', e.message); // 用户取消操作,不做处理
proxy.$modal?.msgError?.('撤回失败,后端服务可能未支持此功能');
} }
}).catch(() => {});
}; };
/** /**
* 打印申请单 * 打印申请单 - 已校对/待接收/已接收/已检查状态可用
* 打印内容与详情展示一致,并包含申请单条码
*/ */
const handlePrint = (row) => { const handlePrint = async (row) => {
// 使用浏览器原生打印功能 try {
window.print(); proxy.$modal?.msgInfo?.('正在生成打印预览...');
// 确保科室数据已加载,用于解析发往科室名称
if (!orgOptions.value || orgOptions.value.length === 0) {
await new Promise((resolve) => {
getDepartmentList().then((res) => {
orgOptions.value = res.data || [];
resolve();
});
});
}
// 解析 descJson
let descData = {};
if (row.descJson) {
try {
const obj = JSON.parse(row.descJson);
// 将发往科室ID转换为名称
if (obj.targetDepartment) {
obj.targetDepartment = recursionFun(obj.targetDepartment);
}
descData = obj;
} catch (e) {
console.error('解析 descJson 失败:', e);
}
}
// 构建诊疗项目表格行
let detailRowsHtml = '';
const detailList = row.requestFormDetailList || [];
if (detailList.length > 0) {
detailList.forEach((item, index) => {
detailRowsHtml += `
<tr>
<td style="text-align:center;padding:6px 8px;border:1px solid #ddd;">${index + 1}</td>
<td style="padding:6px 8px;border:1px solid #ddd;">${item.adviceName || '-'}</td>
<td style="text-align:center;padding:6px 8px;border:1px solid #ddd;">${item.quantity || '-'}</td>
<td style="padding:6px 8px;border:1px solid #ddd;">${item.unitCode_dictText || '-'}</td>
<td style="text-align:right;padding:6px 8px;border:1px solid #ddd;">${item.totalPrice != null ? '¥' + Number(item.totalPrice).toFixed(2) : '-'}</td>
</tr>`;
});
}
// 构建 descJson 字段行(与详情弹窗展示的字段一致)
const fieldKeys = ['targetDepartment', 'symptom', 'sign', 'clinicalDiagnosis', 'otherDiagnosis', 'relatedResult', 'attention'];
let descFieldsHtml = '';
fieldKeys.forEach((key) => {
const label = labelMap[key] || key;
if (descData[key] != null && descData[key] !== '') {
descFieldsHtml += `
<div class="info-row">
<span class="label">${label}</span>
<span class="value">${descData[key]}</span>
</div>`;
}
});
// 构建完整打印HTML
const printContent = `
<div class="print-wrapper">
<!-- 标题 -->
<div class="print-header">
<div class="print-title">检查申请单</div>
<div class="print-meta">打印时间:${new Date().toLocaleString()}</div>
</div>
<!-- 基本信息 -->
<div class="print-section">
<div class="section-title">基本信息</div>
<div class="info-grid">
<div class="info-row"><span class="label">患者姓名:</span><span class="value">${row.patientName || '-'}</span></div>
<div class="info-row"><span class="label">申请单名称:</span><span class="value">${row.name || '-'}</span></div>
<div class="info-row"><span class="label">申请单状态:</span><span class="value">${parseStatus(row.status)}</span></div>
<div class="info-row"><span class="label">创建时间:</span><span class="value">${row.createTime || '-'}</span></div>
<div class="info-row"><span class="label">申请单号:</span><span class="value">${row.prescriptionNo || '-'}</span></div>
<div class="info-row"><span class="label">申请者:</span><span class="value">${row.requesterId_dictText || '-'}</span></div>
<div class="info-row"><span class="label">就诊ID</span><span class="value">${row.encounterId || '-'}</span></div>
<div class="info-row"><span class="label">申请单ID</span><span class="value">${row.requestFormId || '-'}</span></div>
</div>
</div>
${descFieldsHtml ? `
<!-- 申请单描述 -->
<div class="print-section">
<div class="section-title">申请单描述</div>
${descFieldsHtml}
</div>` : ''}
${detailRowsHtml ? `
<!-- 诊疗项目 -->
<div class="print-section">
<div class="section-title">诊疗项目</div>
<table class="detail-table">
<thead>
<tr>
<th style="width:50px;padding:8px;border:1px solid #ddd;background:#f5f7fa;text-align:center;">序号</th>
<th style="padding:8px;border:1px solid #ddd;background:#f5f7fa;text-align:left;">医嘱名称</th>
<th style="width:60px;padding:8px;border:1px solid #ddd;background:#f5f7fa;text-align:center;">数量</th>
<th style="width:60px;padding:8px;border:1px solid #ddd;background:#f5f7fa;text-align:center;">单位</th>
<th style="width:80px;padding:8px;border:1px solid #ddd;background:#f5f7fa;text-align:right;">总价</th>
</tr>
</thead>
<tbody>${detailRowsHtml}</tbody>
</table>
</div>` : ''}
<!-- 条码区 -->
<div class="barcode-section">
<div class="barcode-container">
<div class="barcode-number">${row.prescriptionNo || ''}</div>
<div class="barcode-label">申请单号 / 扫码核验</div>
</div>
</div>
<div class="print-footer">
<div class="footer-line">本申请单仅供院内使用,请勿外传</div>
</div>
</div>
`;
// 打开新窗口打印
const printWindow = window.open('', '_blank');
if (!printWindow) {
proxy.$modal?.msgError?.('无法打开打印窗口,请检查浏览器弹窗设置');
return;
}
printWindow.document.write(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>检查申请单 - 打印</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif;
font-size: 12px;
color: #333;
padding: 20px;
}
.print-wrapper {
max-width: 210mm;
margin: 0 auto;
}
.print-header {
text-align: center;
padding-bottom: 12px;
margin-bottom: 16px;
border-bottom: 2px solid #333;
}
.print-title {
font-size: 20px;
font-weight: bold;
letter-spacing: 4px;
margin-bottom: 6px;
}
.print-meta {
font-size: 11px;
color: #666;
}
.print-section {
margin-bottom: 16px;
}
.section-title {
font-size: 14px;
font-weight: bold;
margin-bottom: 10px;
padding-bottom: 4px;
border-bottom: 1px solid #ddd;
}
.info-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 6px 16px;
}
.info-row {
font-size: 12px;
line-height: 1.8;
}
.info-row .label {
font-weight: 600;
color: #555;
}
.info-row .value {
color: #333;
}
.detail-table {
width: 100%;
border-collapse: collapse;
margin-top: 6px;
}
.detail-table th {
font-size: 11px;
font-weight: 600;
color: #555;
}
.detail-table td {
font-size: 12px;
}
.barcode-section {
margin-top: 24px;
padding-top: 16px;
border-top: 1px dashed #ccc;
text-align: center;
}
.barcode-container {
display: inline-block;
padding: 12px 24px;
border: 2px solid #333;
border-radius: 6px;
background: #fff;
}
.barcode-number {
font-family: 'Libre Barcode 128', 'Libre Barcode 39', 'Code128', 'C', monospace;
font-size: 42px;
letter-spacing: 4px;
color: #000;
line-height: 1.2;
}
.barcode-label {
font-size: 10px;
color: #888;
margin-top: 4px;
letter-spacing: 2px;
}
.print-footer {
margin-top: 20px;
text-align: center;
}
.footer-line {
font-size: 10px;
color: #aaa;
}
@media print {
body { padding: 0; }
.print-wrapper { max-width: none; }
.barcode-section { page-break-inside: avoid; }
}
</style>
</head>
<body>
${printContent}
</body>
</html>
`);
printWindow.document.close();
// 等待内容渲染后打印
printWindow.onload = function() {
setTimeout(() => {
printWindow.print();
proxy.$modal?.closeAll?.();
}, 300);
};
} catch (error) {
console.error('打印失败:', error);
proxy.$modal?.msgError?.('打印失败: ' + (error.message || '未知错误'));
}
}; };
/** /**
* 查看检查报告 * 查看报告 - 仅已出报告状态可用
* 调用报告查询接口弹窗展示结构化报告或影像预览PACS链接
*/ */
const handleViewReport = async (row) => { const handleViewReport = async (row) => {
reportRow.value = row;
reportDialogVisible.value = true;
reportLoading.value = true;
reportData.value = null;
try { try {
const res = await getTestResult({ encounterId: row.encounterId || patientInfo.value?.encounterId }); const res = await getTestResult({ prescriptionNo: row.prescriptionNo });
if (res?.code === 200 && res.data) { if (res.code === 200) {
const reportUrl = Array.isArray(res.data) ? res.data[0]?.reportUrl : res.data?.reportUrl; if (res.data) {
if (reportUrl) { // 支持两种返回格式:
window.open(reportUrl, '_blank'); // 1. res.data 为对象(结构化报告):含 patientName, prescriptionNo, reportTime, diagnosis/content, imageUrl/pacsUrl
} else { // 2. res.data 为字符串报告URL映射到 imageUrl 以支持iframe预览
proxy.$modal?.msgWarning?.('暂无检查报告'); if (typeof res.data === 'string') {
reportData.value = {
prescriptionNo: row.prescriptionNo,
imageUrl: res.data,
};
} else if (typeof res.data === 'object') {
reportData.value = {
...res.data,
prescriptionNo: res.data.prescriptionNo || row.prescriptionNo,
};
} }
} else { } else {
proxy.$modal?.msgWarning?.('暂无检查报告'); reportData.value = null;
}
} else {
reportData.value = null;
proxy.$modal?.msgWarning?.(res.msg || '暂未生成报告');
} }
} catch (e) { } catch (e) {
console.warn('查看检查报告失败:', e.message); console.error('获取报告失败:', e);
proxy.$modal?.msgError?.('获取检查报告失败'); reportData.value = null;
proxy.$modal?.msgError?.('获取报告失败');
} finally {
reportLoading.value = false;
}
};
/**
* 打开PACS影像链接
*/
const openPacsLink = () => {
const url = reportData.value?.pacsUrl || reportData.value?.imageUrl;
if (url) {
window.open(url, '_blank');
}
};
// ========== 修改申请单相关 ==========
const editDialogVisible = ref(false);
const editFormRef = ref(null);
const editingRow = ref(null);
// 修改申请单 - 复用检查申请单弹窗
const handleEdit = (row) => {
editingRow.value = { ...row };
editDialogVisible.value = true;
// 弹窗打开后手动调用getList确保数据加载
nextTick(() => {
editFormRef.value?.getList?.();
editFormRef.value?.getLocationInfo?.();
editFormRef.value?.getDiagnosisList?.();
});
};
// 编辑弹窗确认提交
const handleEditSubmit = () => {
// 调用MedicalExaminations组件的submit方法
if (editFormRef.value?.submit) {
editFormRef.value.submit();
} }
}; };
@@ -626,4 +1053,96 @@ defineExpose({
overflow: auto; overflow: auto;
} }
} }
// 报告弹窗样式
.report-viewer {
min-height: 200px;
padding: 8px 0;
}
.report-viewer-container {
.report-content-section {
margin-top: 16px;
padding: 12px;
background: #fafafa;
border-radius: 6px;
border: 1px solid #eee;
.section-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 10px;
color: #303133;
display: flex;
align-items: center;
}
.report-content-text {
font-size: 13px;
line-height: 1.8;
color: #606266;
white-space: pre-wrap;
}
}
.report-image-section {
margin-top: 16px;
.section-title {
font-size: 14px;
font-weight: 600;
margin-bottom: 10px;
color: #303133;
display: flex;
align-items: center;
}
.report-iframe {
width: 100%;
height: 480px;
border: 1px solid #dcdfe6;
border-radius: 4px;
}
}
}
// 状态标签样式 - 参考临床医嘱
:deep(.el-tag) {
border-radius: 2px;
padding: 0 8px;
height: 24px;
line-height: 22px;
font-size: 12px;
border-width: 1px;
}
:deep(.el-tag--info.is-plain) {
background: #f4f4f5;
border-color: #e9e9eb;
color: #909399;
}
:deep(.el-tag--primary.is-plain) {
background: #ecf5ff;
border-color: #d9ecff;
color: #409eff;
}
:deep(.el-tag--success.is-plain) {
background: #f0f9eb;
border-color: #e1f3d8;
color: #67c23a;
}
:deep(.el-tag--warning.is-plain) {
background: #fdf6ec;
border-color: #faecd8;
color: #e6a23c;
}
:deep(.el-tag--danger.is-plain) {
background: #fef0f0;
border-color: #fde2e2;
color: #f56c6c;
}
</style> </style>

View File

@@ -249,7 +249,7 @@ const submit = () => {
requestFormId: '', // 申请单ID requestFormId: '', // 申请单ID
name: '输血申请单', name: '输血申请单',
descJson: JSON.stringify(form), descJson: JSON.stringify(form),
categoryEnum: '3', // 1 检验 2 检查 3 输血 4 手术 categoryEnum: '23', // 21 检验 22 检查 23 输血 24 手术(避开 adviceType 1-6 碰撞)
}).then((res) => { }).then((res) => {
if (res.code === 200) { if (res.code === 200) {
proxy.$message.success(res.msg); proxy.$message.success(res.msg);

View File

@@ -316,7 +316,7 @@ const submit = () => {
requestFormId: '', // 申请单ID requestFormId: '', // 申请单ID
name: '检验申请单', name: '检验申请单',
descJson: JSON.stringify(form), descJson: JSON.stringify(form),
categoryEnum: '1', // 1 检验 2 检查 3 输血 4 手术 categoryEnum: '21', // 21 检验 22 检查 23 输血 24 手术(避开 adviceType 1-6 碰撞)
}; };
saveInspection(params).then((res) => { saveInspection(params).then((res) => {
if (res.code === 200) { if (res.code === 200) {

View File

@@ -227,7 +227,7 @@
</template> </template>
<script setup name="MedicalExaminations"> <script setup name="MedicalExaminations">
import {getCurrentInstance, onMounted, reactive, ref, watch, computed} from 'vue'; import {getCurrentInstance, onMounted, reactive, ref, watch, computed, nextTick} from 'vue';
import {patientInfo} from '../../../store/patient.js'; import {patientInfo} from '../../../store/patient.js';
import {getDepartmentList} from '@/api/public.js'; import {getDepartmentList} from '@/api/public.js';
import {getEncounterDiagnosis} from '../../api.js'; import {getEncounterDiagnosis} from '../../api.js';
@@ -251,7 +251,26 @@ const findTreeItem = (list, id) => {
}; };
const emits = defineEmits(['submitOk']); const emits = defineEmits(['submitOk']);
const props = defineProps({}); const props = defineProps({
isEditMode: {
type: Boolean,
default: false,
},
editData: {
type: Object,
default: () => ({}),
},
// 支持通过props传入patientInfo
externalPatientInfo: {
type: Object,
default: null,
},
});
// 优先使用外部传入的patientInfo否则使用store中的
const effectivePatientInfo = computed(() => {
return props.externalPatientInfo || patientInfo.value;
});
const orgOptions = ref([]); const orgOptions = ref([]);
const state = reactive({}); const state = reactive({});
const applicationListAll = ref(); const applicationListAll = ref();
@@ -272,7 +291,9 @@ const isSevereAllergy = computed(() => {
}); });
const getList = () => { const getList = () => {
if (!patientInfo.value?.inHospitalOrgId) { console.log('getList called, effectivePatientInfo:', effectivePatientInfo.value);
if (!effectivePatientInfo.value?.inHospitalOrgId) {
console.log('inHospitalOrgId is missing, setting empty list');
applicationList.value = []; applicationList.value = [];
return; return;
} }
@@ -281,7 +302,7 @@ const getList = () => {
pageSize: 500, pageSize: 500,
pageNum: 1, pageNum: 1,
categoryCode: '23', categoryCode: '23',
organizationId: patientInfo.value.inHospitalOrgId, organizationId: effectivePatientInfo.value.inHospitalOrgId,
adviceTypes: [3], adviceTypes: [3],
}) })
.then((res) => { .then((res) => {
@@ -298,6 +319,24 @@ const getList = () => {
key: item.adviceDefinitionId, key: item.adviceDefinitionId,
}; };
}); });
// 编辑模式下,加载完数据后设置已选项目
if (props.isEditMode && props.editData?.requestFormDetailList?.length) {
nextTick(() => {
// 使用 adviceName 匹配
const selectedNames = props.editData.requestFormDetailList.map(item => item.adviceName);
console.log('getList completed, selectedNames:', selectedNames);
const selectedIds = [];
applicationList.value?.forEach(app => {
// 匹配时去掉价格部分,只比较名称
const appName = app.label?.split(' (')[0];
if (selectedNames.includes(appName)) {
selectedIds.push(app.key);
}
});
console.log('getList completed, matched selectedIds:', selectedIds);
transferValue.value = selectedIds;
});
}
} else { } else {
proxy.$message.error(res.message); proxy.$message.error(res.message);
applicationList.value = []; applicationList.value = [];
@@ -387,12 +426,65 @@ const handleSyncHistory = async () => {
// 自动带入患者过敏史 // 自动带入患者过敏史
const loadPatientAllergyHistory = () => { const loadPatientAllergyHistory = () => {
if (!patientInfo.value?.patientId) return; if (!effectivePatientInfo.value?.patientId) return;
};
// 加载编辑数据
const loadEditData = () => {
if (!props.isEditMode || !props.editData?.requestFormId) return;
console.log('loadEditData called, editData:', props.editData);
// 解析已有的descJson填充表单
if (props.editData.descJson) {
try {
const obj = JSON.parse(props.editData.descJson);
form.targetDepartment = obj.targetDepartment || '';
form.urgencyLevel = obj.urgencyLevel || 'routine';
form.allergyHistory = obj.allergyHistory || '';
form.examinationPurpose = obj.examinationPurpose || '';
form.medicalHistorySummary = obj.medicalHistorySummary || '';
form.expectedExaminationTime = obj.expectedExaminationTime || '';
form.symptom = obj.symptom || '';
form.sign = obj.sign || '';
form.clinicalDiagnosis = obj.clinicalDiagnosis || '';
form.otherDiagnosis = obj.otherDiagnosis || '';
form.relatedResult = obj.relatedResult || '';
form.attention = obj.attention || '';
form.allergyConfirmed = obj.allergyConfirmed || false;
} catch (e) {
console.error('解析descJson失败:', e);
}
}
// 设置已选项目从requestFormDetailList获取
// 注意:后端返回的字段是 adviceName不是 adviceDefinitionId
console.log('requestFormDetailList:', props.editData.requestFormDetailList);
if (props.editData.requestFormDetailList && props.editData.requestFormDetailList.length) {
// 使用 adviceName 匹配
const selectedNames = props.editData.requestFormDetailList.map(item => item.adviceName);
console.log('setting transferValue by adviceName to:', selectedNames);
// 通过名称匹配找到对应的 key
const selectedIds = [];
applicationList.value?.forEach(app => {
if (selectedNames.includes(app.label?.split(' (')[0])) {
selectedIds.push(app.key);
}
});
console.log('matched selectedIds:', selectedIds);
transferValue.value = selectedIds;
} else {
console.log('requestFormDetailList is empty or undefined');
}
}; };
const projectWithDepartment = (selectProjectIds, type) => { const projectWithDepartment = (selectProjectIds, type) => {
let isRelease = true; let isRelease = true;
const arr = []; const arr = [];
// 确保 applicationList 存在
if (!applicationList.value || !Array.isArray(applicationList.value)) {
return true;
}
selectProjectIds.forEach((element) => { selectProjectIds.forEach((element) => {
const searchData = applicationList.value.find((item) => { const searchData = applicationList.value.find((item) => {
return element == item.adviceDefinitionId; return element == item.adviceDefinitionId;
@@ -429,9 +521,28 @@ const projectWithDepartment = (selectProjectIds, type) => {
}; };
watch(() => transferValue.value, (newValue) => { watch(() => transferValue.value, (newValue) => {
console.log('transferValue changed:', newValue);
console.log('applicationList length:', applicationList.value?.length);
projectWithDepartment(newValue, 1); projectWithDepartment(newValue, 1);
}); });
// 监听 applicationList 加载完成,重新设置已选项目
watch(() => applicationList.value, (newList) => {
if (newList && newList.length > 0 && props.isEditMode && props.editData?.requestFormDetailList?.length) {
console.log('applicationList loaded, re-setting transferValue');
// 使用 adviceName 匹配
const selectedNames = props.editData.requestFormDetailList.map(item => item.adviceName);
const selectedIds = [];
newList.forEach(app => {
const appName = app.label?.split(' (')[0];
if (selectedNames.includes(appName)) {
selectedIds.push(app.key);
}
});
transferValue.value = selectedIds;
}
});
const getPriorityCode = () => { const getPriorityCode = () => {
return form.urgencyLevel === 'emergency' ? 1 : 0; return form.urgencyLevel === 'emergency' ? 1 : 0;
}; };
@@ -470,28 +581,32 @@ const submit = () => {
adviceType: item.adviceType, adviceType: item.adviceType,
definitionId: item.priceList[0].definitionId, definitionId: item.priceList[0].definitionId,
definitionDetailId: item.priceList[0].definitionDetailId, definitionDetailId: item.priceList[0].definitionDetailId,
accountId: patientInfo.value.accountId, accountId: effectivePatientInfo.value.accountId,
}; };
}); });
const submitForm = { ...form, priorityCode: getPriorityCode() }; const submitForm = { ...form, priorityCode: getPriorityCode() };
console.log('提交 descJson:', JSON.stringify(submitForm)); console.log('提交 descJson:', JSON.stringify(submitForm));
// 如果是编辑模式带上requestFormId
const requestFormId = props.isEditMode ? props.editData?.requestFormId : '';
saveCheckd({ saveCheckd({
activityList: applicationListAllFilter, activityList: applicationListAllFilter,
patientId: patientInfo.value.patientId, patientId: effectivePatientInfo.value.patientId,
encounterId: patientInfo.value.encounterId, encounterId: effectivePatientInfo.value.encounterId,
organizationId: patientInfo.value.inHospitalOrgId, organizationId: effectivePatientInfo.value.inHospitalOrgId,
requestFormId: '', requestFormId: requestFormId,
name: transferValue.value.map(id => { name: applicationListAllFilter.map(item => item.adviceName).join('、'),
const item = applicationListAll.value?.find(i => i.adviceDefinitionId === id);
return item?.adviceName || '';
}).filter(Boolean).join('、'),
descJson: JSON.stringify(submitForm), descJson: JSON.stringify(submitForm),
categoryEnum: '2', categoryEnum: '22',
}).then((res) => { }).then((res) => {
if (res.code === 200) { if (res.code === 200) {
if (props.isEditMode) {
proxy.$message.success(res.msg || '修改成功');
} else {
proxy.$message.success(res.msg); proxy.$message.success(res.msg);
}
applicationList.value = []; applicationList.value = [];
resetForm(); resetForm();
emits('submitOk'); emits('submitOk');
@@ -527,7 +642,7 @@ const getLocationInfo = () => {
}; };
function getDiagnosisList() { function getDiagnosisList() {
getEncounterDiagnosis(patientInfo.value.encounterId).then((res) => { getEncounterDiagnosis(effectivePatientInfo.value.encounterId).then((res) => {
if (res.code == 200) { if (res.code == 200) {
const datas = (res.data || []).map((item) => { const datas = (res.data || []).map((item) => {
let obj = { ...item }; let obj = { ...item };
@@ -548,9 +663,24 @@ onMounted(() => {
getList(); getList();
getLocationInfo(); getLocationInfo();
loadPatientAllergyHistory(); loadPatientAllergyHistory();
loadEditData();
}); });
defineExpose({ state, submit, getLocationInfo, getDiagnosisList, resetForm }); // 监听编辑模式变化,重新加载数据
watch(
() => props.isEditMode,
(newVal) => {
if (newVal) {
nextTick(() => {
getList();
getLocationInfo();
loadEditData();
});
}
}
);
defineExpose({ state, submit, getLocationInfo, getDiagnosisList, resetForm, getList });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -267,7 +267,7 @@ const submit = () => {
requestFormId: '', // 申请单ID requestFormId: '', // 申请单ID
name: '手术申请单', name: '手术申请单',
descJson: JSON.stringify(form), descJson: JSON.stringify(form),
categoryEnum: '4', // 1 检验 2 检查 3 输血 4 手术 categoryEnum: '24', // 21 检验 22 检查 23 输血 24 手术(避开 adviceType 1-6 碰撞)
}).then((res) => { }).then((res) => {
if (res.code === 200) { if (res.code === 200) {
proxy.$message.success(res.msg); proxy.$message.success(res.msg);

View File

@@ -1225,7 +1225,7 @@ function handleSave() {
groupId: item.groupId, groupId: item.groupId,
uniqueKey: undefined, uniqueKey: undefined,
// 确保 therapyEnum 被正确传递 // 确保 therapyEnum 被正确传递
therapyEnum: parsedContent.therapyEnum || item.therapyEnum || '1', therapyEnum: parsedContent?.therapyEnum || item.therapyEnum || '1',
}; };
}); });
} catch (error) { } catch (error) {

View File

@@ -519,17 +519,31 @@ watch(
} }
); );
// 加载科室选项 // 加载科室选项(支持树形/扁平两种数据结构)
function loadDepartmentOptions() { function loadDepartmentOptions() {
getOrgList() getOrgList()
.then((res) => { .then((res) => {
if (res.data && res.data.records && res.data.records.length > 0) { if (res.data) {
const firstRecord = res.data.records[0]; // 尝试从树形结构中取records[0].children
// 优先使用 children树形结构回退到 records 本身(扁平结构) if (res.data.records && res.data.records.length > 0) {
departmentOptions.value = (firstRecord.children && firstRecord.children.length > 0) if (res.data.records[0].children && res.data.records[0].children.length > 0) {
? firstRecord.children departmentOptions.value = res.data.records[0].children;
: res.data.records; return;
} }
// 如果 records[0] 有 id 和 name非树根节点直接用所有 records
if (res.data.records[0].id) {
departmentOptions.value = res.data.records;
return;
}
}
// 兜底:如果 records 不存在或为空,尝试直接使用 data 本身
if (Array.isArray(res.data)) {
departmentOptions.value = res.data;
return;
}
}
// 所有方式都失败,置空
departmentOptions.value = [];
}) })
.catch(() => { .catch(() => {
console.warn('科室列表加载失败(可能无权限)'); console.warn('科室列表加载失败(可能无权限)');
@@ -593,12 +607,14 @@ function getItemType_Text(type) {
} }
function getUnitCodeOptions(row) { function getUnitCodeOptions(row) {
const unitCodes = [ const unitCodes = [
{ code: String(row.unitCode), codeText: row.unitCode_dictText }, { code: row.unitCode != null ? String(row.unitCode) : null, codeText: row.unitCode_dictText },
{ code: String(row.minUnitCode), codeText: row.minUnitCode_dictText }, { code: row.minUnitCode != null ? String(row.minUnitCode) : null, codeText: row.minUnitCode_dictText },
].filter(item => item.code); ];
// 过滤掉 code 为空的单位选项
const validUnitCodes = unitCodes.filter(item => item.code != null && item.code !== '');
// 使用 Set 来跟踪已经存在的 code // 使用 Set 来跟踪已经存在的 code
const seenCodes = new Set(); const seenCodes = new Set();
const uniqueUnitCodes = unitCodes.filter((item) => { const uniqueUnitCodes = validUnitCodes.filter((item) => {
// 如果 Set 中没有这个 code就保留它并把它加入 Set // 如果 Set 中没有这个 code就保留它并把它加入 Set
if (!seenCodes.has(item.code)) { if (!seenCodes.has(item.code)) {
seenCodes.add(item.code); seenCodes.add(item.code);
@@ -667,14 +683,36 @@ function selectChange(row) {
defaultUnitCode = String(row.minUnitCode); defaultUnitCode = String(row.minUnitCode);
} else if (row.unitCode) { } else if (row.unitCode) {
defaultUnitCode = String(row.unitCode); defaultUnitCode = String(row.unitCode);
} else if (row.minUnitCode_dictText) {
// 兜底:如果 minUnitCode 为空但字典文本存在,使用文本作为选项值
defaultUnitCode = row.minUnitCode_dictText;
} else if (row.unitCode_dictText) {
defaultUnitCode = row.unitCode_dictText;
}
// 如果默认单位不在 uniqueUnitCodes 中,添加兜底选项
if (defaultUnitCode && !uniqueUnitCodes.some(u => u.code === defaultUnitCode)) {
uniqueUnitCodes.push({ code: defaultUnitCode, codeText: defaultUnitCode });
} }
// 设置默认执行科室/位置(统一转为字符串,避免 el-select 类型不匹配) // 设置默认执行科室/位置(统一转为字符串,避免 el-select 类型不匹配)
let defaultPositionId = undefined; let defaultPositionId = undefined;
if (row.adviceType === 3 && departmentOptions.value.length > 0) { if (row.adviceType === 3 && departmentOptions.value.length > 0) {
// 诊疗:优先使用患者所在科室,否则取第一个科室 // 诊疗:
const patientOrgId = props.patientInfo.organizationId; // 1. 优先使用诊疗目录项目的"所属科室"row.orgId
const matched = departmentOptions.value.find(d => String(d.id) === String(patientOrgId)); // 2. 其次使用患者当前病房科室patientInfo.organizationId
defaultPositionId = matched ? String(matched.id) : String(departmentOptions.value[0].id); // 3. 最后取第一个科室
const orgIdPriority = [row.orgId, props.patientInfo.organizationId];
for (const id of orgIdPriority) {
if (id) {
const matched = departmentOptions.value.find(d => String(d.id) === String(id));
if (matched) {
defaultPositionId = String(matched.id);
break;
}
}
}
if (!defaultPositionId) {
defaultPositionId = String(departmentOptions.value[0].id);
}
} else if (row.adviceType === 2 && locationOptions.value.length > 0) { } else if (row.adviceType === 2 && locationOptions.value.length > 0) {
// 耗材:默认取第一个药房/耗材房 // 耗材:默认取第一个药房/耗材房
defaultPositionId = String(locationOptions.value[0].value); defaultPositionId = String(locationOptions.value[0].value);

View File

@@ -458,7 +458,9 @@ const loadPatientInfo = () => {
'YYYY-MM-DD HH:mm:ss' 'YYYY-MM-DD HH:mm:ss'
); );
} else { } else {
interventionForm.value.startTime = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss'); // 已有患者entranceType == 1不自动填充当前时间避免覆盖历史数据
// 新入科患者由后端默认返回当前时间,或由用户手动选择
interventionForm.value.startTime = '';
} }
interventionForm.value.height = res.data.height; interventionForm.value.height = res.data.height;
interventionForm.value.weight = res.data.weight; interventionForm.value.weight = res.data.weight;