bug 362 428 436

This commit is contained in:
Ranyunqiao
2026-05-13 12:59:03 +08:00
parent 5bc8a8e517
commit b7df71fd0b
13 changed files with 207 additions and 25 deletions

View File

@@ -59,6 +59,16 @@ public interface IDoctorStationAdviceAppService {
*/
R<?> getRequestBaseInfo(Long encounterId);
/**
* 查询医嘱请求数据(支持按生成来源和来源单据号过滤)
*
* @param encounterId 就诊id
* @param generateSourceEnum 生成来源(可选,如手术计费=6
* @param sourceBillNo 来源业务单据号(可选)
* @return 医嘱请求数据
*/
R<?> getRequestBaseInfo(Long encounterId, Integer generateSourceEnum, String sourceBillNo);
/**
* 门诊签退医嘱
*

View File

@@ -1987,13 +1987,25 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
*/
@Override
public R<?> getRequestBaseInfo(Long encounterId) {
return this.getRequestBaseInfo(encounterId, null, null);
}
@Override
public R<?> getRequestBaseInfo(Long encounterId, Integer generateSourceEnum, String sourceBillNo) {
// 当前账号的参与者id
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
// 未指定generateSourceEnum时默认只查询医生开立的医嘱
int sourceEnum = (generateSourceEnum != null) ? generateSourceEnum : GenerateSource.DOCTOR_PRESCRIPTION.getValue();
// 医嘱请求数据
List<RequestBaseDto> requestBaseInfo = doctorStationAdviceAppMapper.getRequestBaseInfo(encounterId, null,
CommonConstants.TableName.MED_MEDICATION_REQUEST, CommonConstants.TableName.WOR_DEVICE_REQUEST,
CommonConstants.TableName.WOR_SERVICE_REQUEST, practitionerId, Whether.NO.getCode(),
GenerateSource.DOCTOR_PRESCRIPTION.getValue());
sourceEnum, sourceBillNo);
// 手术计费场景sourceBillNo 不为空时只保留诊疗请求3/6过滤掉药品1和耗材2
if (sourceBillNo != null && !sourceBillNo.isEmpty()) {
requestBaseInfo.removeIf(dto -> dto.getAdviceType() != null
&& (dto.getAdviceType() == 1 || dto.getAdviceType() == 2));
}
for (RequestBaseDto requestBaseDto : requestBaseInfo) {
// 请求状态
requestBaseDto
@@ -2114,7 +2126,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
List<RequestBaseDto> requestBaseInfo = doctorStationAdviceAppMapper.getRequestBaseInfo(encounterId, patientId,
CommonConstants.TableName.MED_MEDICATION_REQUEST, CommonConstants.TableName.WOR_DEVICE_REQUEST,
CommonConstants.TableName.WOR_SERVICE_REQUEST, practitionerId, Whether.YES.getCode(),
GenerateSource.DOCTOR_PRESCRIPTION.getValue());
GenerateSource.DOCTOR_PRESCRIPTION.getValue(), null);
for (RequestBaseDto requestBaseDto : requestBaseInfo) {
// 请求状态
requestBaseDto

View File

@@ -112,11 +112,16 @@ public class DoctorStationAdviceController {
* 查询医嘱请求数据
*
* @param encounterId 就诊id
* @param generateSourceEnum 生成来源(可选,用于按来源过滤,如手术计费=6
* @param sourceBillNo 来源业务单据号(可选,用于按来源单据过滤)
* @return 医嘱请求数据
*/
@GetMapping(value = "/request-base-info")
public R<?> getRequestBaseInfo(@RequestParam(required = false) Long encounterId) {
return iDoctorStationAdviceAppService.getRequestBaseInfo(encounterId);
public R<?> getRequestBaseInfo(
@RequestParam(required = false) Long encounterId,
@RequestParam(required = false) Integer generateSourceEnum,
@RequestParam(required = false) String sourceBillNo) {
return iDoctorStationAdviceAppService.getRequestBaseInfo(encounterId, generateSourceEnum, sourceBillNo);
}
/**

View File

@@ -122,7 +122,8 @@ public interface DoctorStationAdviceAppMapper {
@Param("MED_MEDICATION_REQUEST") String MED_MEDICATION_REQUEST,
@Param("WOR_DEVICE_REQUEST") String WOR_DEVICE_REQUEST,
@Param("WOR_SERVICE_REQUEST") String WOR_SERVICE_REQUEST, @Param("practitionerId") Long practitionerId,
@Param("historyFlag") String historyFlag, @Param("generateSourceEnum") Integer generateSourceEnum);
@Param("historyFlag") String historyFlag, @Param("generateSourceEnum") Integer generateSourceEnum,
@Param("sourceBillNo") String sourceBillNo);
/**
* 查询就诊费用性质

View File

@@ -16,6 +16,7 @@ import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.List;
/**
@@ -142,6 +143,32 @@ public class RequestFormManageController {
return R.ok(iRequestFormManageAppService.getRequestForm(encounterId, ActivityDefCategory.PROCEDURE.getCode()));
}
/**
* 分页查询手术申请单全局不需要encounterId用于门诊手术安排查找弹窗
*
* @param surgeryNo 手术单号(模糊查询,可选)
* @param applyTimeStart 申请时间起始(可选)
* @param applyTimeEnd 申请时间截止(可选)
* @param mainDoctorId 主刀医生ID可选
* @param applyDeptId 申请科室ID可选
* @param pageNo 页码默认1
* @param pageSize 每页数量默认10
* @return 分页手术申请单列表
*/
@GetMapping(value = "/get-surgery-page")
public R<IPage<RequestFormPageDto>> getSurgeryRequestFormPage(
@RequestParam(required = false) String surgeryNo,
@RequestParam(required = false) LocalDate applyTimeStart,
@RequestParam(required = false) LocalDate applyTimeEnd,
@RequestParam(required = false) Long mainDoctorId,
@RequestParam(required = false) Long applyDeptId,
@RequestParam(defaultValue = "1") Integer pageNo,
@RequestParam(defaultValue = "10") Integer pageSize) {
RequestFormDto dto = new RequestFormDto(surgeryNo, null, applyTimeStart, applyTimeEnd,
mainDoctorId, applyDeptId, pageNo, pageSize);
return R.ok(iRequestFormManageAppService.getRequestFormPage(dto));
}
/**
* 分页查询申请单
* @return 申请单
*/

View File

@@ -527,6 +527,9 @@
LEFT JOIN cli_condition AS cc ON cc.id = T1.condition_id AND cc.delete_flag = '0'
LEFT JOIN cli_condition_definition AS ccd ON ccd.id = cc.definition_id
WHERE T1.delete_flag = '0' AND T1.tcm_flag = 0
<if test="generateSourceEnum != null">
AND T1.generate_source_enum = #{generateSourceEnum}
</if>
<if test="historyFlag == '0'.toString()">
AND T1.encounter_id = #{encounterId}
</if>
@@ -695,6 +698,9 @@
-- based_on_table='med_medication_request' → 输液/皮试执行记录,应排除
-- based_on_table='exam_apply'/'lab_apply' → 申请单原始医嘱,应保留
AND (T1.based_on_id IS NULL OR T1.based_on_table IS NULL OR T1.based_on_table NOT IN ('med_medication_request', 'med_medication_dispense'))
<if test="sourceBillNo != null and sourceBillNo != ''">
AND T1.prescription_no = #{sourceBillNo}
</if>
<if test="historyFlag == '0'.toString()">
AND T1.encounter_id = #{encounterId}
</if>

View File

@@ -163,7 +163,7 @@ export function updateCheckPart(data) {
// 查询检查套餐列表
export function listCheckPackage(query) {
return request({
url: '/system/check-package/list',
url: '/system/package/list',
method: 'get',
params: query
})

View File

@@ -58,10 +58,17 @@ export function singOut(data) {
/**
* 获取患者本次就诊处方
*/
export function getPrescriptionList(encounterId) {
// Add timestamp to bypass browser caching and ensure fresh data is loaded
export function getPrescriptionList(encounterId, generateSourceEnum, sourceBillNo) {
let url = '/doctor-station/advice/request-base-info?encounterId=' + encounterId
if (generateSourceEnum != null) {
url += '&generateSourceEnum=' + generateSourceEnum
}
if (sourceBillNo != null) {
url += '&sourceBillNo=' + encodeURIComponent(sourceBillNo)
}
url += '&t=' + Date.now()
return request({
url: '/doctor-station/advice/request-base-info?encounterId=' + encounterId + '&t=' + Date.now(),
url: url,
method: 'get',
})
}

View File

@@ -381,6 +381,14 @@ const props = defineProps({
activeTab: {
type: String,
},
generateSourceEnum: {
type: Number,
default: null,
},
sourceBillNo: {
type: String,
default: null,
},
});
const isAdding = ref(false);
const isSaving = ref(false); // #437 防重复提交锁
@@ -468,7 +476,11 @@ watch(
function getListInfo(addNewRow) {
isAdding.value = false;
getPrescriptionList(props.patientInfo.encounterId).then((res) => {
getPrescriptionList(
props.patientInfo.encounterId,
props.generateSourceEnum ?? undefined,
props.sourceBillNo ?? undefined,
).then((res) => {
// 为每行数据添加 adviceTypeValue 字段,用于类型下拉框显示
prescriptionList.value = (res.data || []).map(item => {
// 根据 adviceType 和 categoryCode 找到对应的 adviceTypeValue

View File

@@ -402,6 +402,21 @@
<span class="method-price">¥{{ method.packagePrice || item.price }}</span>
</el-checkbox>
</div>
<!-- 选中方法后显示对应的套餐明细 -->
<div v-if="item.selectedMethod && item.methodPackageDetails && item.methodPackageDetails.length > 0" class="method-package-details">
<div class="method-package-header">
<span class="method-package-title">套餐明细 - {{ item.selectedMethod.name }}</span>
</div>
<div v-for="detail in item.methodPackageDetails" :key="detail.id" class="method-option">
<el-checkbox v-model="detail.checked">
<span class="method-name">{{ detail.name }}</span>
<span class="method-price">数量: {{ detail.quantity }} ¥{{ detail.price }}</span>
</el-checkbox>
</div>
</div>
<div v-if="item.selectedMethod && item.methodPackageLoading" class="method-package-loading">
加载套餐明细中...
</div>
</div>
</div>
</div>
@@ -419,7 +434,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
import { Printer, Delete, ArrowDown, ArrowUp, Close } from '@element-plus/icons-vue';
import useUserStore from '@/store/modules/user';
import request from '@/utils/request';
import { listCheckMethod, searchCheckMethod } from '@/api/system/checkType';
import { listCheckMethod, searchCheckMethod, listCheckPackage } from '@/api/system/checkType';
import { getEncounterDiagnosis } from '../api.js';
const props = defineProps({
@@ -445,7 +460,7 @@ async function loadPackageDetails(row, treeNode, resolve) {
}
try {
const res = await request({
url: `/exam/package/${row.packageId}/details`,
url: `/system/package/${row.packageId}/details`,
method: 'get'
});
if (res.code === 200 && res.data) {
@@ -474,7 +489,7 @@ async function loadPackageDetailsForItem(item) {
}
try {
const res = await request({
url: `/exam/package/${item.packageId}/details`,
url: `/system/package/${item.packageId}/details`,
method: 'get'
});
if (res.code === 200 && res.data) {
@@ -1250,21 +1265,68 @@ async function toggleItemExpand(item) {
}
// Bug #384修复: 勾选框选择检查方法(单选逻辑)
function selectMethodCheckbox(checked, item, method) {
async function selectMethodCheckbox(checked, item, method) {
if (checked) {
item.selectedMethod = method;
// 动态加载该方法对应的套餐明细
await loadMethodPackageDetails(item, method);
} else {
item.selectedMethod = null;
item.methodPackageDetails = [];
}
// 联动更新表单检查方法显示字段
updateMethodDisplay();
// #430: 套餐金额实时同步到申请单
nextTick(() => {
form.totalAmount = totalAmountCalc.value;
});
}
// 根据检查方法的packageName加载对应的套餐明细
async function loadMethodPackageDetails(item, method) {
item.methodPackageLoading = true;
item.methodPackageDetails = [];
try {
if (!method.packageName) {
item.methodPackageLoading = false;
return;
}
// 通过packageName查询套餐获取packageId
const pkgRes = await listCheckPackage({ packageName: method.packageName });
let packages = pkgRes?.data || [];
if (!Array.isArray(packages)) {
packages = packages.records || packages.data || [];
}
if (packages.length === 0) {
item.methodPackageLoading = false;
return;
}
const packageId = packages[0].id;
// 查询套餐明细
const detailRes = await request({
url: `/system/package/${packageId}/details`,
method: 'get'
});
if (detailRes.code === 200 && detailRes.data) {
item.methodPackageDetails = detailRes.data.map(d => ({
id: d.id,
name: d.itemName || d.name,
quantity: d.quantity || 1,
unit: d.unit || '次',
price: d.unitPrice || d.price || 0,
amount: d.amount || d.total || 0,
checked: true // 默认勾选
}));
}
} catch (err) {
console.error('加载方法套餐明细失败:', err);
item.methodPackageDetails = [];
} finally {
item.methodPackageLoading = false;
}
}
// Bug #384修复: 更新检查方法显示字段(联动)
function updateMethodDisplay() {
// 找到第一个有选中检查方法的项目
@@ -1670,6 +1732,29 @@ defineExpose({ getList });
margin-left: 8px;
}
/* 选中方法后显示的套餐明细 */
.method-package-details {
margin-top: 4px;
padding: 4px 0;
border-top: 1px dashed #dcdfe6;
}
.method-package-header {
padding: 2px 0 4px 24px;
}
.method-package-title {
font-size: 10px;
color: #909399;
font-weight: 500;
}
.method-package-loading {
padding: 4px 0 4px 24px;
font-size: 10px;
color: #c0c4cc;
}
/* 折叠组件细节 */
:deep(.el-collapse) {
border: none;

View File

@@ -44,6 +44,17 @@ export function getSurgery(queryParams) {
});
}
/**
* 分页查询手术申请单全局不需要encounterId用于门诊手术安排查找弹窗
*/
export function getSurgeryPage(queryParams) {
return request({
url: '/reg-doctorstation/request-form/get-surgery-page',
method: 'get',
params: queryParams,
});
}
/**
* 查询护理医嘱信息
*/

View File

@@ -453,6 +453,10 @@ const loadPatientInfo = () => {
interventionForm.value.startTime = dayjs(res.data.startTime).format(
'YYYY-MM-DD HH:mm:ss'
);
} else if (res.data.inHosTime) {
interventionForm.value.startTime = dayjs(res.data.inHosTime).format(
'YYYY-MM-DD HH:mm:ss'
);
} else {
interventionForm.value.startTime = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss');
}

View File

@@ -829,7 +829,9 @@
</el-descriptions>
</div>
<div style="padding: 10px">
<prescriptionlist v-if="showChargeDialog" :patientInfo="chargePatientInfo" ref="prescriptionRef" />
<prescriptionlist v-if="showChargeDialog" :patientInfo="chargePatientInfo" ref="prescriptionRef"
:generateSourceEnum="1"
:sourceBillNo="chargePatientInfo.sourceBillNo" />
<div class="overlay" v-if="disabled"></div>
</div>
</div>
@@ -877,12 +879,12 @@ import {
addSurgerySchedule,
updateSurgerySchedule,
deleteSurgerySchedule,
getSurgeryScheduleDetail,
getSurgeryApplyList
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 { getTenantPage } from '@/api/system/tenant'
import { getContract } from '@/views/inpatientDoctor/home/components/api.js'
import SurgeryCharge from '../charge/surgerycharge/index.vue'
@@ -1394,8 +1396,8 @@ async function handleChargeCharge(row) {
orgId: userStore.organizationId || userStore.orgId || userStore.tenantId || 1,
// 添加账户ID
accountId: accountId,
// 添加手术申请单号用于追溯
sourceBillNo: row.applyId,
// 添加手术单号用于关联对应的手术医嘱
sourceBillNo: row.operCode,
//添加计费标志手术计费
generateSourceEnum: 6
}
@@ -1456,7 +1458,8 @@ function handleMedicalAdvice(row) {
role: userStore.roles[0],
effectiveOrgId : row.effectiveOrgId,
orgId: userStore.orgId,
positionId: userStore.orgId
positionId: userStore.orgId,
applyId: row.applyId // 手术申请单ID用于过滤关联医嘱
}
// 🔧 关键修复:如果已有提交的医嘱数据,并且是同一个患者的就诊,则使用保存的数据
@@ -1754,7 +1757,7 @@ function handleQuoteBilling() {
// 重新拉取计费药品数据
if (temporaryPatientInfo.value.visitId) {
temporaryMedicalLoading.value = true // 🔧 新增:开始加载
getPrescriptionList(temporaryPatientInfo.value.visitId).then((res) => {
getPrescriptionList(temporaryPatientInfo.value.visitId, 6, temporaryPatientInfo.value.operCode).then((res) => {
if (res.code === 200 && res.data) {
// 🔧 修复:先清空旧数据,避免数据累积
temporaryBillingMedicines.value = []
@@ -2029,7 +2032,7 @@ function handleFindApply() {
getSurgicalScheduleList()
}
// 获取手术申请列表(用于查找”弹窗)
// 获取手术申请列表(用于查找”弹窗)
function getSurgicalScheduleList() {
applyLoading.value = true
const params = { ...applyQueryParams }
@@ -2038,8 +2041,7 @@ function getSurgicalScheduleList() {
params.applyTimeEnd = params.applyTimeRange[1]
delete params.applyTimeRange
}
getSurgeryApplyList(params).then((res) => {
// Check if data is nested under data.data or directly under data
getSurgeryPage(params).then((res) => {
const responseData = res.data.data || res.data
applyList.value = responseData.records || []
applyTotal.value = responseData.total || 0