bug 443 522 523

This commit is contained in:
Ranyunqiao
2026-05-18 10:16:57 +08:00
committed by guanyu
parent af9b3bbc76
commit a89d91c5be
5 changed files with 97 additions and 33 deletions

View File

@@ -559,9 +559,11 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
if (adviceSaveList != null && !adviceSaveList.isEmpty()) { if (adviceSaveList != null && !adviceSaveList.isEmpty()) {
for (int i = 0; i < adviceSaveList.size(); i++) { for (int i = 0; i < adviceSaveList.size(); i++) {
AdviceSaveDto dto = adviceSaveList.get(i); AdviceSaveDto dto = adviceSaveList.get(i);
log.info("Request[{}]: requestId={}, dbOpType={}, adviceType={}, encounterId={}, patientId={}", log.info("Request[{}]: requestId={}, dbOpType={}, adviceType={}, encounterId={}, patientId={}, categoryEnum={}, categoryEnum.class={}, categoryCode={}, categoryCode.class={}",
i, dto.getRequestId(), dto.getDbOpType(), dto.getAdviceType(), i, dto.getRequestId(), dto.getDbOpType(), dto.getAdviceType(),
dto.getEncounterId(), dto.getPatientId()); dto.getEncounterId(), dto.getPatientId(),
dto.getCategoryEnum(), dto.getCategoryEnum() != null ? dto.getCategoryEnum().getClass().getName() : "NULL",
dto.getCategoryCode(), dto.getCategoryCode() != null ? dto.getCategoryCode().getClass().getName() : "NULL");
} }
} }
@@ -1562,7 +1564,7 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// 🔧 BugFix #498: categoryEnum=22(检查) 走 ServiceRequest不走 DeviceRequest // 🔧 BugFix #498: categoryEnum=22(检查) 走 ServiceRequest不走 DeviceRequest
// 检查申请单的诊疗定义ID存在 activityId不在 adviceDefinitionId // 检查申请单的诊疗定义ID存在 activityId不在 adviceDefinitionId
// deviceDefId 对应耗材定义ID不能用诊疗定义ID填充 // deviceDefId 对应耗材定义ID不能用诊疗定义ID填充
if (adviceSaveDto.getCategoryEnum() == 22) { if (Integer.valueOf(22).equals(adviceSaveDto.getCategoryEnum())) {
log.info("handDevice skip - 检查申请单(categoryEnum=22) 走 ServiceRequest 路径,跳过 DeviceRequest 保存"); log.info("handDevice skip - 检查申请单(categoryEnum=22) 走 ServiceRequest 路径,跳过 DeviceRequest 保存");
continue; // 跳过本次循环,不走耗材请求路径 continue; // 跳过本次循环,不走耗材请求路径
} else if (adviceSaveDto.getAdviceDefinitionId() != null) { } else if (adviceSaveDto.getAdviceDefinitionId() != null) {

View File

@@ -3801,6 +3801,8 @@ function handleSaveHistory(value) {
uniqueKey: undefined, uniqueKey: undefined,
dbOpType: value.requestId ? '2' : '1', dbOpType: value.requestId ? '2' : '1',
minUnitQuantity: value.quantity * value.partPercent, minUnitQuantity: value.quantity * value.partPercent,
// 🔧 修复:确保 categoryEnum 被传递(耗材必填字段),避免后端 NPE
categoryEnum: value.categoryEnum || value.categoryCode,
conditionId: conditionId.value, conditionId: conditionId.value,
conditionDefinitionId: conditionDefinitionId.value, conditionDefinitionId: conditionDefinitionId.value,
encounterDiagnosisId: encounterDiagnosisId.value, encounterDiagnosisId: encounterDiagnosisId.value,

View File

@@ -63,7 +63,7 @@
<span class="medicine-info"> 诊断{{ config.diagnosisName }} </span> <span class="medicine-info"> 诊断{{ config.diagnosisName }} </span>
<span class="medicine-info"> 皮试{{ row.skinTestFlag_enumText }} </span> <span class="medicine-info"> 皮试{{ row.skinTestFlag_enumText }} </span>
<span class="medicine-info"> 注射药品{{ row.injectFlag_enumText }} </span> <span class="medicine-info"> 注射药品{{ row.injectFlag_enumText }} </span>
<span class="total-amount" v-if="row.therapyEnum == '2'"> <span class="total-amount">
总金额{{ row.totalPrice ? Number(row.totalPrice).toFixed(2) + ' 元' : '0.00 元' }} 总金额{{ row.totalPrice ? Number(row.totalPrice).toFixed(2) + ' 元' : '0.00 元' }}
</span> </span>
</div> </div>
@@ -83,7 +83,7 @@
:controls="false" :controls="false"
style="width: 70px" style="width: 70px"
:ref="(el) => setInputRef('doseQuantity', el)" :ref="(el) => setInputRef('doseQuantity', el)"
@input="convertValues" @input="() => { convertValues(); calculateTotalAmount(); }"
@keyup.enter.prevent="handleEnter('doseQuantity')" @keyup.enter.prevent="handleEnter('doseQuantity')"
/> />
</el-form-item> </el-form-item>
@@ -110,7 +110,7 @@
:controls="false" :controls="false"
style="width: 70px; margin-left: 32px" style="width: 70px; margin-left: 32px"
:ref="(el) => setInputRef('dose', el)" :ref="(el) => setInputRef('dose', el)"
@input="convertDoseValues" @input="() => { convertDoseValues(); calculateTotalAmount(); }"
@keyup.enter.prevent="handleEnter('dose')" @keyup.enter.prevent="handleEnter('dose')"
/> />
</el-form-item> </el-form-item>
@@ -119,7 +119,7 @@
v-model="row.doseUnitCode" v-model="row.doseUnitCode"
style="width: 70px" style="width: 70px"
placeholder=" " placeholder=" "
@change="convertValues" @change="() => { convertValues(); calculateTotalAmount(); }"
> >
<el-option <el-option
v-for="item in row.unitCodeList" v-for="item in row.unitCodeList"
@@ -271,13 +271,17 @@
controls-position="right" controls-position="right"
:controls="false" :controls="false"
:ref="(el) => setInputRef('firstDose', el)" :ref="(el) => setInputRef('firstDose', el)"
@input="calculateTotalAmount"
@keyup.enter.prevent="handleEnter('firstDose')" @keyup.enter.prevent="handleEnter('firstDose')"
/> />
</el-form-item> </el-form-item>
<el-select v-model="row.unitCode" style="width: 70px" placeholder=" "> <el-select v-model="row.doseUnitCode" style="width: 70px" placeholder=" ">
<template v-for="item in row.unitCodeList" :key="item.value"> <el-option
<el-option v-if="checkUnit(item)" :value="item.value" :label="item.label" /> v-for="item in row.unitCodeList"
</template> :value="item.value"
:label="item.label"
:key="item.value"
/>
</el-select> </el-select>
</template> </template>
</div> </div>
@@ -426,6 +430,7 @@
<script setup lang="ts"> <script setup lang="ts">
import {computed, getCurrentInstance, nextTick, onMounted, ref, watch} from 'vue'; import {computed, getCurrentInstance, nextTick, onMounted, ref, watch} from 'vue';
import Decimal from 'decimal.js';
interface Config { interface Config {
diagnosisName: string; // 仅用于显示 diagnosisName: string; // 仅用于显示
@@ -623,7 +628,18 @@ const orgFallbackOption = (value: any) => {
const convertValues = () => props.handlers.convertValue('doseQuantity', props.row, props.index); const convertValues = () => props.handlers.convertValue('doseQuantity', props.row, props.index);
const convertDoseValues = () => props.handlers.convertValue('dose', props.row, props.index); const convertDoseValues = () => props.handlers.convertValue('dose', props.row, props.index);
const calculateTotalPrice = () => props.handlers.calculateTotal('price', props.row, props.index); const calculateTotalPrice = () => props.handlers.calculateTotal('price', props.row, props.index);
const calculateTotalAmount = () => props.handlers.calculateTotal('amount', props.row, props.index); // 直接用 row 计算总金额:数量 * 单价,避免父组件索引不匹配的问题
const calculateTotalAmount = () => {
nextTick(() => {
const row = props.row;
const qty = new Decimal(row.doseQuantity || 0);
const isMinUnit = row.unitCode == row.minUnitCode;
const price = isMinUnit ? row.minUnitPrice : row.unitPrice;
// 四舍五入到2位再算与页面显示的单价一致
const roundedPrice = new Decimal(price || 0).toDecimalPlaces(2, Decimal.ROUND_HALF_UP);
row.totalPrice = qty.mul(roundedPrice).toFixed(6);
});
};
const setInputRef = props.handlers.setInputRef; const setInputRef = props.handlers.setInputRef;
defineExpose({ defineExpose({

View File

@@ -605,13 +605,26 @@ function getListInfo(addNewRow) {
prescriptionList.value = res.data prescriptionList.value = res.data
.map((item) => { .map((item) => {
const parsedContent = JSON.parse(item.contentJson); const parsedContent = JSON.parse(item.contentJson);
// 构造 unitCodeList确保编辑时下拉框有正确的选项
console.log('【DEBUG】unitCode:', parsedContent?.unitCode, typeof parsedContent?.unitCode, 'unitCodeList:', JSON.stringify(parsedContent?.unitCodeList));
const unitCodeListData = parsedContent?.unitCodeList || [
{ value: String(parsedContent?.unitCode ?? item.unitCode ?? ''), label: parsedContent?.unitCode_dictText ?? item.unitCode_dictText ?? '', type: 'unit' },
{ value: String(parsedContent?.doseUnitCode ?? ''), label: parsedContent?.doseUnitCode_dictText ?? '', type: 'dose' },
{ value: String(parsedContent?.minUnitCode ?? ''), label: parsedContent?.minUnitCode_dictText ?? '', type: 'minUnit' },
];
return { return {
...parsedContent, ...parsedContent,
...item, ...item,
// 🔧 修复contentJson 中的 totalPrice 优先于 charge_item 表的 totalPrice
// charge_item.totalPrice 可能为 0 或 null新建医嘱时导致总金额显示为 "-"
totalPrice: parsedContent?.totalPrice || item.totalPrice,
isEdit: false, isEdit: false,
showPopover: false, showPopover: false,
doseQuantity: parsedContent?.doseQuantity, doseQuantity: parsedContent?.doseQuantity,
doseUnitCode_dictText: parsedContent?.doseUnitCode_dictText, doseUnitCode_dictText: parsedContent?.doseUnitCode_dictText,
// 确保 unitCode 为字符串类型,与 unitCodeList 的 option value 类型一致
unitCode: String(parsedContent?.unitCode ?? item.unitCode ?? ''),
unitCodeList: unitCodeListData,
// 确保 therapyEnum 被正确设置,优先使用 contentJson 中的值 // 确保 therapyEnum 被正确设置,优先使用 contentJson 中的值
therapyEnum: String(parsedContent?.therapyEnum ?? item.therapyEnum ?? '1'), therapyEnum: String(parsedContent?.therapyEnum ?? item.therapyEnum ?? '1'),
// 🔧 修复:确保 orgId 为 String 类型,与 organization 树的 id 类型一致 // 🔧 修复:确保 orgId 为 String 类型,与 organization 树的 id 类型一致
@@ -1622,7 +1635,10 @@ function handleSaveGroup(orderGroupList) {
conditionId: conditionId.value, conditionId: conditionId.value,
conditionDefinitionId: conditionDefinitionId.value, conditionDefinitionId: conditionDefinitionId.value,
encounterDiagnosisId: encounterDiagnosisId.value, encounterDiagnosisId: encounterDiagnosisId.value,
diagnosisName: diagnosisName.value,
therapyEnum: prescriptionList.value[rowIndex.value]?.therapyEnum || mergedDetail.therapyEnum || '1', therapyEnum: prescriptionList.value[rowIndex.value]?.therapyEnum || mergedDetail.therapyEnum || '1',
// 🔧 修复:确保组套医嘱的 categoryEnum 被正确映射,防止后端 NPE
categoryEnum: mergedDetail?.categoryEnum || mergedDetail?.categoryCode || item?.categoryCode,
}; };
// 计算价格和总量 // 计算价格和总量
@@ -1663,6 +1679,8 @@ function handleSaveHistory(value) {
encounterDiagnosisId: encounterDiagnosisId.value, encounterDiagnosisId: encounterDiagnosisId.value,
// 确保 therapyEnum 被正确传递,默认为长期医嘱('1') // 确保 therapyEnum 被正确传递,默认为长期医嘱('1')
therapyEnum: value.therapyEnum || '1', therapyEnum: value.therapyEnum || '1',
// 🔧 修复:历史医嘱的 categoryCode(String) 映射为后端 categoryEnum(Integer),防止 NPE
categoryEnum: value.categoryEnum || value.categoryCode,
contentJson: JSON.stringify({ contentJson: JSON.stringify({
...value, ...value,
therapyEnum: value.therapyEnum || '1', therapyEnum: value.therapyEnum || '1',

View File

@@ -847,26 +847,10 @@ function getPatientList() {
patientId: props.patientId patientId: props.patientId
} }
}).then((res) => { }).then((res) => {
if (res.code === 200 && res.data) {
// 判断返回的数据结构 patientList.value = Array.isArray(res.data) ? res.data : (res.data.data || []);
let data = res.data;
if (res.data && res.data.data && typeof res.data.data === 'object') {
// 如果是嵌套结构 {data: {data: Array(3)}}
data = res.data.data;
} else if (res.data && typeof res.data === 'object' && res.data.data !== undefined) {
// 如果是 {code: 200, msg: '操作成功', data: Array(3)}
data = res.data.data;
}
console.log('=== data 长度 ===', data?.length);
if (res.code === 200 && data) {
console.log('=== 准备赋值 patientList.value ===');
patientList.value = data;
console.log('=== patientList.value 赋值后 ===', patientList.value);
console.log('=== patientList.value 长度 ===', patientList.value?.length);
} else { } else {
console.error('=== 查询失败或无数据 ==='); patientList.value = [];
} }
}).catch(err => { }).catch(err => {
console.error('=== 查询报错 ===', err); console.error('=== 查询报错 ===', err);
@@ -885,8 +869,22 @@ function getPatientDetial() {
queryParams.value.patientId = props.patientId; queryParams.value.patientId = props.patientId;
// 默认查询今天的数据 // 默认查询今天的数据
const today = moment().format('YYYY-MM-DD'); const today = moment().format('YYYY-MM-DD');
const now = moment();
receptionTime.value = [today, today]; receptionTime.value = [today, today];
formData.value.recordingDate = today; formData.value.recordingDate = today;
// 自动填充最近的整点时间点2/6/10/14/18/22点
const hour = now.hour();
const timePoints = [2, 6, 10, 14, 18, 22];
let nearestHour = timePoints[0];
let minDiff = Math.abs(hour - nearestHour);
for (const tp of timePoints) {
const diff = Math.abs(hour - tp);
if (diff < minDiff) {
minDiff = diff;
nearestHour = tp;
}
}
formData.value.timePoint = String(nearestHour).padStart(2, '0') + '00';
// 自动加载数据 // 自动加载数据
getPatientList(); getPatientList();
listPatient(queryParams.value).then((res) => { listPatient(queryParams.value).then((res) => {
@@ -967,6 +965,29 @@ function confirmCharge() {
encounterId: props.patientInfo.encounterId, encounterId: props.patientInfo.encounterId,
}; };
// 自动获取当前日期和时间
const now = moment();
if (!params.recordingDate) {
params.recordingDate = now.format('YYYY-MM-DD');
formData.value.recordingDate = params.recordingDate;
}
if (!params.timePoint) {
// 取最近的整点时间点2/6/10/14/18/22点
const hour = now.hour();
const timePoints = [2, 6, 10, 14, 18, 22];
let nearestHour = timePoints[0];
let minDiff = Math.abs(hour - nearestHour);
for (const tp of timePoints) {
const diff = Math.abs(hour - tp);
if (diff < minDiff) {
minDiff = diff;
nearestHour = tp;
}
}
params.timePoint = String(nearestHour).padStart(2, '0') + '00';
formData.value.timePoint = params.timePoint;
}
// 收集所有录入的体征数据 // 收集所有录入的体征数据
const vitalSignsCode = []; const vitalSignsCode = [];
const vitalSignsValues = []; const vitalSignsValues = [];
@@ -1052,9 +1073,14 @@ function confirmCharge() {
vitalSignsValues.push(params.stoolVolume); vitalSignsValues.push(params.stoolVolume);
} }
// 校验:没有录入任何体征数据时提示用户
if (vitalSignsCode.length === 0) {
proxy.$modal.msgWarning('请录入患者体征信息');
return;
}
params.vitalSignsCode = vitalSignsCode; params.vitalSignsCode = vitalSignsCode;
params.vitalSignsValues = vitalSignsValues; params.vitalSignsValues = vitalSignsValues;
params.recordingDate = formData.value.recordingDate || moment(new Date()).format('YYYY-MM-DD');
addVitalSigns(params).then(res => { addVitalSigns(params).then(res => {
if (res.code === 200) { if (res.code === 200) {