1 Commits

Author SHA1 Message Date
荀彧
706c679f68 Fix Bug #535: 【住院护士站-医嘱校对】已校验过的医嘱错误显示于"未校对"列表中,导致数据状态联动失效
根因:后端 getInpatientAdvicePage 方法中将 requestStatus 置为 null,
未按前端 tab 传入的状态值过滤,导致无论切换哪个 tab 都返回全部医嘱。
SQL 中的 CASE 条件仅处理 DRAFT 状态的 performer_check_id 校验,
并未按 request_status 字段过滤。

修复:保存 requestStatus 后,在查询结果集上按 requestStatus 手动过滤,
与 exeStatus 的过滤方式保持一致。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 16:25:51 +08:00
8 changed files with 210 additions and 161 deletions

View File

@@ -83,9 +83,6 @@ public class InfectiousCardDto {
/** 病例分类 */ /** 病例分类 */
private String diseaseType; private String diseaseType;
/** 病例分类 */
private Integer caseClass;
/** 发病日期 */ /** 发病日期 */
private LocalDate onsetDate; private LocalDate onsetDate;

View File

@@ -112,15 +112,12 @@ public class InfectiousDiseaseReportDto {
private Integer caseClass; private Integer caseClass;
/** 发病日期(默认诊断时间,病原携带者填初检日期) */ /** 发病日期(默认诊断时间,病原携带者填初检日期) */
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate onsetDate; private LocalDate onsetDate;
/** 诊断日期(精确到小时) */ /** 诊断日期(精确到小时) */
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
private LocalDateTime diagDate; private LocalDateTime diagDate;
/** 死亡日期(死亡病例必填) */ /** 死亡日期(死亡病例必填) */
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate deathDate; private LocalDate deathDate;
/** 订正病名(订正报告必填) */ /** 订正病名(订正报告必填) */
@@ -139,7 +136,6 @@ public class InfectiousDiseaseReportDto {
private String reportDoc; private String reportDoc;
/** 填卡日期 */ /** 填卡日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate reportDate; private LocalDate reportDate;
/** 报卡名称代码 1-中华人民共和国传染病报告卡 */ /** 报卡名称代码 1-中华人民共和国传染病报告卡 */

View File

@@ -178,7 +178,9 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
inpatientAdviceParam.setEncounterIds(null); inpatientAdviceParam.setEncounterIds(null);
Integer exeStatus = inpatientAdviceParam.getExeStatus(); Integer exeStatus = inpatientAdviceParam.getExeStatus();
inpatientAdviceParam.setExeStatus(null); inpatientAdviceParam.setExeStatus(null);
// requestStatus由前端tab传入通过QueryWrapper自动添加到SQL外层WHERE过滤 // requestStatus由前端tab控制,需在后端过滤
Integer requestStatus = inpatientAdviceParam.getRequestStatus();
inpatientAdviceParam.setRequestStatus(null);
// 构建查询条件 // 构建查询条件
QueryWrapper<InpatientAdviceParam> queryWrapper QueryWrapper<InpatientAdviceParam> queryWrapper
= HisQueryUtils.buildQueryWrapper(inpatientAdviceParam, null, null, null); = HisQueryUtils.buildQueryWrapper(inpatientAdviceParam, null, null, null);
@@ -291,6 +293,16 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
inpatientAdvicePage.setTotal(filteredList.size()); inpatientAdvicePage.setTotal(filteredList.size());
} }
} }
// 按请求状态tab切换过滤医嘱
if (requestStatus != null) {
List<InpatientAdviceDto> statusFilteredList = inpatientAdvicePage.getRecords().stream()
.filter(advice -> requestStatus.equals(advice.getRequestStatus()))
.collect(Collectors.toList());
inpatientAdvicePage.setRecords(statusFilteredList);
inpatientAdvicePage.setTotal(statusFilteredList.size());
}
return R.ok(inpatientAdvicePage); return R.ok(inpatientAdvicePage);
} }
@@ -367,7 +379,7 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
.in(MedicationDispense::getMedReqId, medReqIds) .in(MedicationDispense::getMedReqId, medReqIds)
.eq(MedicationDispense::getStatusEnum, DispenseStatus.COMPLETED.getValue())); .eq(MedicationDispense::getStatusEnum, DispenseStatus.COMPLETED.getValue()));
if (!dispenseList.isEmpty()) { if (!dispenseList.isEmpty()) {
return R.fail("药品已由药房发放,请先执行退药处理,不可直接退回"); return R.fail("医嘱已发药,无法退回");
} }
} }
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId(); Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();

View File

@@ -1046,10 +1046,8 @@ function handleSave() {
chargeItemId: item.chargeItemId, chargeItemId: item.chargeItemId,
}; };
}); });
// 确保 organizationId 不为 undefined手术计费场景下可能缺失 orgId
const orgId = props.patientInfo.orgId || props.patientInfo.effectiveOrgId || 1;
savePrescriptionSign({ savePrescriptionSign({
organizationId: orgId, organizationId: props.patientInfo.orgId,
adviceSaveList: list, adviceSaveList: list,
}).then((res) => { }).then((res) => {
if (res.code === 200) { if (res.code === 200) {
@@ -1066,7 +1064,6 @@ function handleSave() {
} }
}).catch((error) => { }).catch((error) => {
console.error('签发失败:', error); console.error('签发失败:', error);
console.warn('签发操作失败(可能无权限或后端异常):', error?.response?.data?.msg || error?.message);
proxy.$modal.msgError(error?.response?.data?.msg || error?.message || '签发失败,请重试'); proxy.$modal.msgError(error?.response?.data?.msg || error?.message || '签发失败,请重试');
}); });
} }

View File

@@ -1025,11 +1025,7 @@ function parseBirthDate(birthDate) {
function normalizeDate(value) { function normalizeDate(value) {
if (!value) return ''; if (!value) return '';
const datePart = String(value).split(/[T ]/)[0].replace(/\//g, '-'); return String(value).split(/[T ]/)[0];
const parts = datePart.split('-');
if (parts.length !== 3) return datePart;
const [year, month, day] = parts;
return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
} }
function normalizeSex(value) { function normalizeSex(value) {
@@ -1120,7 +1116,7 @@ function showReport(reportData = {}, readOnly = true) {
addressHouse: reportData.addressHouse || '', addressHouse: reportData.addressHouse || '',
patientBelong: reportData.patientBelong || 1, patientBelong: reportData.patientBelong || 1,
occupation: reportData.occupation || '', occupation: reportData.occupation || '',
caseClass: reportData.caseClass != null ? String(reportData.caseClass) : '', caseClass: reportData.diseaseType != null ? String(reportData.diseaseType) : '',
onsetDate: normalizeDate(reportData.onsetDate), onsetDate: normalizeDate(reportData.onsetDate),
diagDate: normalizeDate(reportData.diagDate), diagDate: normalizeDate(reportData.diagDate),
deathDate: normalizeDate(reportData.deathDate), deathDate: normalizeDate(reportData.deathDate),
@@ -1129,7 +1125,7 @@ function showReport(reportData = {}, readOnly = true) {
selectedClassB: diseaseSelection.selectedClassB, selectedClassB: diseaseSelection.selectedClassB,
selectedClassC: diseaseSelection.selectedClassC, selectedClassC: diseaseSelection.selectedClassC,
otherDisease: reportData.otherDisease || (diseaseCode === 'OTHER' ? reportData.diseaseName || '' : ''), otherDisease: reportData.otherDisease || (diseaseCode === 'OTHER' ? reportData.diseaseName || '' : ''),
diseaseType: reportData.diseaseSubtype || reportData.diseaseType || '', diseaseType: reportData.diseaseSubtype || '',
reportOrg: reportData.reportOrg || '', reportOrg: reportData.reportOrg || '',
reportOrgPhone: reportData.reportOrgPhone || '', reportOrgPhone: reportData.reportOrgPhone || '',
reportDoc: reportData.reportDoc || '', reportDoc: reportData.reportDoc || '',
@@ -1470,7 +1466,7 @@ async function buildSubmitData() {
reportDate: formData.reportDate || null, reportDate: formData.reportDate || null,
cardNameCode: 1, // 默认中华人民共和国传染病报告卡 cardNameCode: 1, // 默认中华人民共和国传染病报告卡
registrationSource: 1, // 默认门诊 registrationSource: 1, // 默认门诊
status: null, status: '',
deptId: props.deptId || null, deptId: props.deptId || null,
doctorId: props.doctorId || null, doctorId: props.doctorId || null,
}; };

View File

@@ -380,7 +380,7 @@ const form = ref({
prescriptionList: prescriptionList.value, prescriptionList: prescriptionList.value,
}); });
const adviceQueryParams = ref({ const adviceQueryParams = ref({
adviceType: 1, adviceType: '',
categoryCode: '', // 初始为空,等待加载配置后动态设置 categoryCode: '', // 初始为空,等待加载配置后动态设置
searchKey: '', searchKey: '',
}); });
@@ -533,6 +533,7 @@ const statusOption = [
let loadingInstance = undefined; let loadingInstance = undefined;
onMounted(() => { onMounted(() => {
document.addEventListener('keydown', escKeyListener); document.addEventListener('keydown', escKeyListener);
getList();
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
@@ -573,7 +574,6 @@ function handleTotalAmount() {
} }
}, new Decimal(0)); }, new Decimal(0));
} }
getList();
function getList() { function getList() {
getDiagnosisDefinitionList(queryParams.value).then((res) => { getDiagnosisDefinitionList(queryParams.value).then((res) => {
// prescriptionList.value = res.data.records; // prescriptionList.value = res.data.records;
@@ -585,6 +585,11 @@ function refresh() {
} }
// 获取列表信息 // 获取列表信息
function getListInfo(addNewRow) { function getListInfo(addNewRow) {
// 守护:未选择患者时不发起 API 请求,避免页面加载时循环报错
if (!patientInfo.value || !patientInfo.value.encounterId) {
console.warn('⚠️ getListInfo 跳过:未选择患者');
return;
}
loadingInstance = ElLoading.service({ fullscreen: true }); loadingInstance = ElLoading.service({ fullscreen: true });
setTimeout(() => { setTimeout(() => {
loadingInstance.close(); loadingInstance.close();
@@ -686,7 +691,7 @@ function loadConfiguredCategories() {
nextTick(() => { nextTick(() => {
// 创建新对象触发响应式更新 // 创建新对象触发响应式更新
adviceQueryParams.value = { adviceQueryParams.value = {
adviceType: 1, adviceType: '',
categoryCode: defaultCategoryCode, categoryCode: defaultCategoryCode,
searchKey: '', searchKey: '',
}; };
@@ -710,9 +715,17 @@ function loadConfiguredCategories() {
// 数据过滤 // 数据过滤
const filterPrescriptionList = computed(() => { const filterPrescriptionList = computed(() => {
const pList = prescriptionList.value.filter((item) => { const pList = prescriptionList.value.filter((item) => {
// 修复 Bug #488orderClassCode 可能是复合值 '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 ( return (
(!therapyEnum.value || therapyEnum.value == item.therapyEnum) && (!therapyEnum.value || therapyEnum.value == item.therapyEnum) &&
(!orderClassCode.value || orderClassCode.value == item.adviceType) && matchAdviceType &&
(!orderStatus.value || (orderStatus.value == item.statusEnum && item.requestId)) (!orderStatus.value || (orderStatus.value == item.statusEnum && item.requestId))
); );
}); });
@@ -740,13 +753,29 @@ function getRowDisabled(row) {
/** /**
* 将行的 adviceType + categoryCode 映射为 el-select 的选中值 * 将行的 adviceType + categoryCode 映射为 el-select 的选中值
* 药品子分类使用复合值如 '1-2'adviceType-categoryCode诊疗/手术/全部使用原始值 * 药品子分类使用复合值如 '1-2'adviceType-categoryCode诊疗/手术/全部使用原始值
* 修复 Bug #488当行的 adviceType 在当前配置中找不到匹配选项时,返回最接近的可用值,避免回显为纯数字
*/ */
function getRowSelectValue(row) { function getRowSelectValue(row) {
if (row.adviceType == 1 && row.categoryCode) { if (row.adviceType == 1 && row.categoryCode) {
return '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 row.adviceType; return row.adviceType;
} }
// 诊疗/手术等非药品类型,检查其值是否在选项列表中
if (adviceTypeList.value.some(item => item.value === row.adviceType)) {
return row.adviceType;
}
// 不在选项中的值(如已废弃的 adviceType返回 undefined 让 el-select 显示为空
return undefined;
}
// 新增医嘱 // 新增医嘱
function handleAddPrescription() { function handleAddPrescription() {
@@ -802,8 +831,8 @@ function clickRowDb(row, column, event) {
return; return;
} }
row.showPopover = false; row.showPopover = false;
// 待签发(已保存 requestId存在)”允许编辑;仅“待保存(无requestId)”允许编辑 // 仅”待签发(statusEnum==1)”允许编辑;”已签发(statusEnum==2)”及之后状态不允许编辑
if (row.statusEnum == 1 && !row.requestId) { if (row.statusEnum == 1) {
// 确保治疗类型为字符串,方便与单选框 label 对齐,默认为长期医嘱('1') // 确保治疗类型为字符串,方便与单选框 label 对齐,默认为长期医嘱('1')
row.therapyEnum = String(row.therapyEnum ?? '1'); row.therapyEnum = String(row.therapyEnum ?? '1');
row.isEdit = true; row.isEdit = true;
@@ -881,9 +910,9 @@ function handleFocus(row, index) {
// 用 adviceType + categoryCode 组合查找匹配的选项 // 用 adviceType + categoryCode 组合查找匹配的选项
const selectValue = (adviceType == 1 && row.categoryCode) ? '1-' + row.categoryCode : adviceType; const selectValue = (adviceType == 1 && row.categoryCode) ? '1-' + row.categoryCode : adviceType;
const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType); const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType);
// If the row has an explicit adviceType (saved/existing row), use its own categoryCode. // 修复Bug #486当行没有显式选择医嘱类型时row.adviceType为undefined
// If no type is selected (new row), use empty string for global search across all categories. // 不传categoryCode让搜索在全药库中进行只有行已选择类型时才用对应categoryCode过滤
const categoryCode = selectedItem ? selectedItem.categoryCode : (row.adviceType != null ? (row.categoryCode || '') : ''); const categoryCode = row.adviceType !== undefined ? (selectedItem ? selectedItem.categoryCode : '') : '';
const searchKey = row.adviceName || ''; const searchKey = row.adviceName || '';
nextTick(() => { nextTick(() => {
@@ -920,9 +949,12 @@ function handleChange(value) {
// 用 adviceType + categoryCode 组合查找匹配的选项 // 用 adviceType + categoryCode 组合查找匹配的选项
const selectValue = (adviceType == 1 && row?.categoryCode) ? '1-' + row.categoryCode : adviceType; const selectValue = (adviceType == 1 && row?.categoryCode) ? '1-' + row.categoryCode : adviceType;
const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType); const selectedItem = adviceTypeList.value.find(item => item.value === selectValue) || adviceTypeList.value.find(item => item.adviceType === adviceType);
// 修复Bug #486当行没有显式选择医嘱类型时不传categoryCode让搜索在全药库中进行 // 修复Bug #486当行没有显式选择医嘱类型时row?.adviceType为undefined
const categoryCode = selectedItem ? selectedItem.categoryCode : (row?.adviceType !== undefined ? (adviceQueryParams.value.categoryCode || '') : ''); // 不传categoryCode让搜索在全药库中进行只有行已选择类型时才用对应categoryCode过滤
tableRef.refresh(adviceType, categoryCode, value); const categoryCode = row?.adviceType !== undefined ? (selectedItem ? selectedItem.categoryCode : '') : '';
// 修复Bug #453当adviceType为空字符串或NaN时不传具体类型让refresh函数根据searchKey决定搜索范围
const effectiveAdviceType = (adviceType && !isNaN(Number(adviceType))) ? adviceType : '';
tableRef.refresh(effectiveAdviceType, categoryCode, value);
} }
} }
} }
@@ -1181,8 +1213,10 @@ function handleSave() {
}); });
// 此处签发处方和单行保存处方传参相同后台已经将传参存为JSON字符串此处直接转换为JSON即可 // 此处签发处方和单行保存处方传参相同后台已经将传参存为JSON字符串此处直接转换为JSON即可
loading.value = true; loading.value = true;
let list = saveList.map((item) => { let list = [];
const parsedContent = JSON.parse(item.contentJson); try {
list = saveList.map((item) => {
const parsedContent = item.contentJson ? JSON.parse(item.contentJson) || {} : {};
return { return {
...parsedContent, ...parsedContent,
adviceType: item.adviceType, adviceType: item.adviceType,
@@ -1191,9 +1225,15 @@ 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) {
loading.value = false;
isSaving.value = false;
proxy.$modal.msgError('医嘱内容解析失败,请检查待签发医嘱');
return;
}
// 保存签发按钮 // 保存签发按钮
isSaving.value = true; isSaving.value = true;
console.log('签发处方参数:', { console.log('签发处方参数:', {
@@ -1208,9 +1248,16 @@ function handleSave() {
if (res.code === 200) { if (res.code === 200) {
proxy.$modal.msgSuccess('签发成功'); proxy.$modal.msgSuccess('签发成功');
isSaving.value = false; 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); getListInfo(false);
bindMethod.value = {}; bindMethod.value = {};
nextId.value == 1; nextId.value = 1;
} else { } else {
proxy.$modal.msgError(res.message); proxy.$modal.msgError(res.message);
isSaving.value = false; isSaving.value = false;
@@ -1311,11 +1358,12 @@ function handleCancelEdit(row, index) {
function handleSaveSign(row, index) { function handleSaveSign(row, index) {
if (row.adviceType != 2) { if (row.adviceType != 2) {
// 修复 Bug #488严格校验 itemNo确保非空且为有效字符串才发起请求
let itemNo = row.adviceType == 1 ? row.methodCode : row.adviceDefinitionId; let itemNo = row.adviceType == 1 ? row.methodCode : row.adviceDefinitionId;
if (!itemNo) { if (!itemNo || String(itemNo).trim() === '') {
console.warn('绑定设备检查跳过itemNo为空adviceType=' + row.adviceType + ', adviceName=' + row.adviceName + ''); console.warn('绑定设备检查跳过itemNo为空adviceType=' + row.adviceType + ', adviceName=' + row.adviceName + '');
} else { } else {
getBindDevice({ typeCode: row.adviceType, itemNo: itemNo }).then((res) => { getBindDevice({ typeCode: row.adviceType, itemNo: String(itemNo) }).then((res) => {
if (res.data.length == 0) { if (res.data.length == 0) {
return; return;
} }
@@ -1376,13 +1424,21 @@ function handleSaveSign(row, index) {
savePrescription({ regAdviceSaveList: [row] }).then((res) => { savePrescription({ regAdviceSaveList: [row] }).then((res) => {
if (res.code === 200) { if (res.code === 200) {
proxy.$modal.msgSuccess('保存成功'); proxy.$modal.msgSuccess('保存成功');
nextId.value == 1; nextId.value = 1;
} }
}); });
} else { } else {
if (prescriptionList.value[0].adviceName) { // 新增行:调用保存接口将数据持久化到后端
handleAddPrescription(); row.dbOpType = '1';
savePrescription({ regAdviceSaveList: [row] }).then((res) => {
if (res.code === 200) {
proxy.$modal.msgSuccess('保存成功');
nextId.value = 1;
// 保存成功后刷新列表,确保后端返回的数据带 requestId
getListInfo(false);
} }
});
// 不需要再添加空行,保存成功后由 getListInfo 处理
} }
adviceQueryParams.value.adviceType = undefined; adviceQueryParams.value.adviceType = undefined;
} }
@@ -1423,32 +1479,40 @@ function handleSaveBatch() {
.then((res) => { .then((res) => {
if (res.code === 200) { if (res.code === 200) {
proxy.$modal.msgSuccess('保存成功'); proxy.$modal.msgSuccess('保存成功');
// 修复#405:保存成功后重置所有待保存行的 isEdit 为 false锁定医嘱不再编辑 // 修复 Bug #405保存成功后锁定所有待保存行,避免医嘱条目仍处于可编辑状态
// saveList 中的 item 与 prescriptionList 是同一对象引用,直接修改即可
saveList.forEach(item => { saveList.forEach(item => {
const row = prescriptionList.value.find(r => r.uniqueKey === item.uniqueKey); item.isEdit = false;
if (row) row.isEdit = false;
}); });
// 兜底:锁定所有 statusEnum == 1 的行,确保没有遗漏
prescriptionList.value.forEach(row => {
if (row.statusEnum == 1) {
row.isEdit = false;
}
});
expandOrder.value = [];
getListInfo(false); getListInfo(false);
nextId.value == 1; nextId.value = 1;
isSaving.value = false; isSaving.value = false;
} }
}) })
.catch((error) => { .catch((error) => {
isSaving.value = false; isSaving.value = false;
proxy.$modal.msgError(error?.msg || '保存失败,请重试');
}); });
} }
function setValue(row) { function setValue(row) {
// 构造单位列表 // 构造单位列表,确保 value 始终为 String 类型,避免 el-select 值类型不匹配
unitCodeList.value = [ unitCodeList.value = [
{ value: row.unitCode, label: row.unitCode_dictText, type: 'unit' }, { value: String(row.unitCode ?? ''), label: row.unitCode_dictText, type: 'unit' },
{ {
value: row.doseUnitCode, value: String(row.doseUnitCode ?? ''),
label: row.doseUnitCode_dictText, label: row.doseUnitCode_dictText,
type: 'dose', type: 'dose',
}, },
{ {
value: row.minUnitCode, value: String(row.minUnitCode ?? ''),
label: row.minUnitCode_dictText, label: row.minUnitCode_dictText,
type: 'minUnit', type: 'minUnit',
}, },
@@ -1513,9 +1577,9 @@ function setValue(row) {
orgName: row.adviceType != 3 ? undefined : (findOrgName(row.orgId || row.positionId || patientInfo.value?.inHospitalOrgId) || row.orgName || patientInfo.value?.inHospitalOrgName || ''), orgName: row.adviceType != 3 ? undefined : (findOrgName(row.orgId || row.positionId || patientInfo.value?.inHospitalOrgId) || row.orgName || patientInfo.value?.inHospitalOrgName || ''),
// dose: undefined, Removed to preserve dose value from group package // dose: undefined, Removed to preserve dose value from group package
unitCodeList: unitCodeList.value, unitCodeList: unitCodeList.value,
doseUnitCode: row.doseUnitCode, doseUnitCode: String(row.doseUnitCode ?? ''),
minUnitCode: row.minUnitCode, minUnitCode: String(row.minUnitCode ?? ''),
unitCode: row.partAttributeEnum == 1 ? row.minUnitCode : row.unitCode, unitCode: row.partAttributeEnum == 1 ? String(row.minUnitCode ?? '') : String(row.unitCode ?? ''),
categoryEnum: row.categoryCode, categoryEnum: row.categoryCode,
definitionId: row.chargeItemDefinitionId, definitionId: row.chargeItemDefinitionId,
executeNum: 1, executeNum: 1,
@@ -1531,6 +1595,10 @@ function setValue(row) {
? new Decimal(selectedStock.price).div(row.partPercent).toFixed(6) ? new Decimal(selectedStock.price).div(row.partPercent).toFixed(6)
: prevRow.minUnitPrice, : prevRow.minUnitPrice,
positionName: selectedStock?.locationName, positionName: selectedStock?.locationName,
// 🔧 Bug #523 修复:初始化 totalPrice 为 0避免总金额列显示为横杠
totalPrice: row.quantity
? new Decimal(row.quantity).mul(selectedStock?.price ?? 0).toFixed(6)
: '0',
} }
: { : {
quantity: 1, quantity: 1,
@@ -1562,17 +1630,21 @@ function handleSaveGroup(orderGroupList) {
// 🔥 新版组件已经预处理了数据,优先使用 mergedDetail // 🔥 新版组件已经预处理了数据,优先使用 mergedDetail
const mergedDetail = item.mergedDetail || { const mergedDetail = item.mergedDetail || {
...(item.orderDetailInfos || {}), ...(item.orderDetailInfos || {}),
adviceName: item.orderDetailInfos?.adviceName || item.orderDefinitionName || '未知项目', adviceName: item.orderDefinitionName || item.orderDetailInfos?.adviceName || '未知项目',
adviceType: item.orderDetailInfos?.adviceType, adviceType: item.orderDetailInfos?.adviceType,
adviceDefinitionId: item.orderDefinitionId || item.orderDetailInfos?.adviceDefinitionId, adviceDefinitionId: item.orderDefinitionId || item.orderDetailInfos?.adviceDefinitionId,
quantity: item.quantity, quantity: item.quantity,
unitCode: item.unitCode || item.orderDetailInfos?.unitCode, unitCode: item.unitCode || item.orderDetailInfos?.unitCode,
unitCodeName: item.unitCodeName, unitCodeName: item.unitCodeName,
dose: item.dose || item.orderDetailInfos?.dose, // 🔧 Bug #403 修复dose/doseQuantity/dispensePerDuration 需用 null 检查,
// 避免组套中值为 null 时回退到医嘱库的 orderDetailInfos
dose: item.dose !== undefined && item.dose !== null ? item.dose : item.orderDetailInfos?.dose,
rateCode: item.rateCode || item.orderDetailInfos?.rateCode, rateCode: item.rateCode || item.orderDetailInfos?.rateCode,
methodCode: item.methodCode || item.orderDetailInfos?.methodCode, methodCode: item.methodCode || item.orderDetailInfos?.methodCode,
dispensePerDuration: item.dispensePerDuration || item.orderDetailInfos?.dispensePerDuration, dispensePerDuration: item.dispensePerDuration !== undefined && item.dispensePerDuration !== null
doseQuantity: item.doseQuantity, ? item.dispensePerDuration : item.orderDetailInfos?.dispensePerDuration,
doseQuantity: item.doseQuantity !== undefined && item.doseQuantity !== null
? item.doseQuantity : item.orderDetailInfos?.doseQuantity,
inventoryList: item.orderDetailInfos?.inventoryList || [], inventoryList: item.orderDetailInfos?.inventoryList || [],
priceList: item.orderDetailInfos?.priceList || [], priceList: item.orderDetailInfos?.priceList || [],
partPercent: item.orderDetailInfos?.partPercent || 1, partPercent: item.orderDetailInfos?.partPercent || 1,
@@ -1591,41 +1663,69 @@ function handleSaveGroup(orderGroupList) {
setValue(mergedDetail); setValue(mergedDetail);
// 创建新的处方项目 // 创建新的处方项目
// 🔧 Bug #403 修复:关键字段使用 null-safe 回退到 mergedDetail已由 setValue 填充完整数据)
// 先取 setValue 填充的行数据作为基础
const baseRow = prescriptionList.value[rowIndex.value];
const newRow = { const newRow = {
...prescriptionList.value[rowIndex.value], ...baseRow,
isEdit: false,
patientId: patientInfo.value.patientId, patientId: patientInfo.value.patientId,
encounterId: patientInfo.value.encounterId, encounterId: patientInfo.value.encounterId,
accountId: accountId.value, accountId: accountId.value,
quantity: item.quantity,
methodCode: item.methodCode,
rateCode: item.rateCode,
dispensePerDuration: item.dispensePerDuration,
dose: item.dose,
doseQuantity: item.doseQuantity,
executeNum: 1, executeNum: 1,
unitCode: item.unitCode,
unitCode_dictText: item.unitCodeName || '',
statusEnum: 1, statusEnum: 1,
orgId: resolveOrgId(item.orderDetailInfos?.orgId || mergedDetail.orgId || patientInfo.value?.inHospitalOrgId) || '', orgId: resolveOrgId(
// 🔧 修复:同时保存 orgName确保树匹配不到时仍有中文名称可显示 (item.orderDetailInfos?.orgId || mergedDetail.orgId)
orgName: findOrgName(item.orderDetailInfos?.orgId || mergedDetail.orgId || patientInfo.value?.inHospitalOrgId) || item.orderDetailInfos?.orgName || mergedDetail.orgName || patientInfo.value?.inHospitalOrgName || '', ? (item.orderDetailInfos?.orgId || mergedDetail.orgId)
: patientInfo.value?.inHospitalOrgId
) || '',
orgName: findOrgName(
(item.orderDetailInfos?.orgId || mergedDetail.orgId)
? (item.orderDetailInfos?.orgId || mergedDetail.orgId)
: patientInfo.value?.inHospitalOrgId
) || item.orderDetailInfos?.orgName || mergedDetail.orgName || patientInfo.value?.inHospitalOrgName || '',
dbOpType: prescriptionList.value[rowIndex.value].requestId ? '2' : '1', dbOpType: prescriptionList.value[rowIndex.value].requestId ? '2' : '1',
conditionId: conditionId.value, conditionId: conditionId.value,
conditionDefinitionId: conditionDefinitionId.value, conditionDefinitionId: conditionDefinitionId.value,
encounterDiagnosisId: encounterDiagnosisId.value, encounterDiagnosisId: encounterDiagnosisId.value,
therapyEnum: prescriptionList.value[rowIndex.value]?.therapyEnum || '1', diagnosisName: diagnosisName.value,
therapyEnum: baseRow?.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 || '';
// 确保价格字段有兜底值setValue 可能因库存不足提前 return 导致未设置)
newRow.unitPrice = baseRow.unitPrice ?? mergedDetail.unitPrice ?? 0;
newRow.minUnitPrice = baseRow.minUnitPrice ?? mergedDetail.minUnitPrice ?? 0;
newRow.unitTempPrice = newRow.unitPrice;
newRow.methodCode_dictText = mergedDetail.methodCode_dictText || item.methodCode_dictText || '';
newRow.rateCode_dictText = mergedDetail.rateCode_dictText || item.rateCode_dictText || '';
newRow.doseUnitCode = mergedDetail.doseUnitCode || item.doseUnitCode || baseRow.doseUnitCode;
newRow.doseUnitCode_dictText = mergedDetail.doseUnitCode_dictText || item.doseUnitCode_dictText || '';
newRow.orgId = mergedDetail.orgId || mergedDetail.positionId || baseRow.orgId || '';
newRow.positionName = mergedDetail.orgName || mergedDetail.positionName || baseRow.positionName || '';
// 计算价格和总量 // 计算价格和总量
const unitInfo = unitCodeList.value.find((k) => k.value == item.unitCode); // 🔧 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;
if (unitInfo && unitInfo.type == 'minUnit') { if (unitInfo && unitInfo.type == 'minUnit') {
newRow.price = newRow.minUnitPrice; newRow.price = newRow.minUnitPrice;
newRow.totalPrice = (item.quantity * newRow.minUnitPrice).toFixed(6); newRow.totalPrice = ((finalQuantity || 0) * newRow.minUnitPrice).toFixed(6);
newRow.minUnitQuantity = item.quantity; newRow.minUnitQuantity = finalQuantity || 0;
} else { } else {
newRow.price = newRow.unitPrice; newRow.price = newRow.unitPrice;
newRow.totalPrice = (item.quantity * newRow.unitPrice).toFixed(6); newRow.totalPrice = ((finalQuantity || 0) * newRow.unitPrice).toFixed(6);
newRow.minUnitQuantity = item.quantity * (item.orderDetailInfos?.partPercent || mergedDetail.partPercent || 1); newRow.minUnitQuantity = (finalQuantity || 0) * partPercent;
} }
newRow.contentJson = JSON.stringify(newRow); newRow.contentJson = JSON.stringify(newRow);

View File

@@ -23,7 +23,7 @@
<span class="descriptions-item-label">全选</span> <span class="descriptions-item-label">全选</span>
<el-switch v-model="chooseAll" @change="handelSwitchChange" /> <el-switch v-model="chooseAll" @change="handelSwitchChange" />
<el-button class="ml20" type="primary" @click="handleCheck"> 核对通过 </el-button> <el-button class="ml20" type="primary" @click="handleCheck"> 核对通过 </el-button>
<el-button class="ml20 mr20" type="danger" :disabled="hasDispensedSelected" @click="handleCancel"> 退回 </el-button> <el-button class="ml20 mr20" type="danger" @click="handleCancel"> 退回 </el-button>
</div> </div>
</div> </div>
<div <div
@@ -152,7 +152,6 @@
</div> </div>
</template> </template>
<script setup> <script setup>
import {ref, computed} from 'vue';
import {adviceVerify, cancel, getPrescriptionList} from './api'; import {adviceVerify, cancel, getPrescriptionList} from './api';
import {patientInfoList} from '../../components/store/patient.js'; import {patientInfoList} from '../../components/store/patient.js';
import {formatDateStr} from '@/utils/index'; import {formatDateStr} from '@/utils/index';
@@ -164,11 +163,6 @@ const type = ref(null);
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance();
const loading = ref(false); const loading = ref(false);
const chooseAll = ref(false); const chooseAll = ref(false);
const selectionTrigger = ref(0);
const hasDispensedSelected = computed(() => {
selectionTrigger.value;
return getSelectRows().some(item => item.dispenseStatus === 4);
});
const props = defineProps({ const props = defineProps({
requestStatus: { requestStatus: {
type: Number, type: Number,
@@ -268,9 +262,7 @@ function getGroupMarkers() {
} }
// 选择框改变时的处理 // 选择框改变时的处理
function handleSelectionChange(selection, row) { function handleSelectionChange(selection, row) {}
selectionTrigger.value++;
}
/** /**
* 核对通过 * 核对通过
@@ -299,7 +291,7 @@ function handleCancel() {
// 校验已发药的医嘱不允许退回 // 校验已发药的医嘱不允许退回
let dispensedItems = list.filter(item => item.dispenseStatus === 4); let dispensedItems = list.filter(item => item.dispenseStatus === 4);
if (dispensedItems.length > 0) { if (dispensedItems.length > 0) {
proxy.$message.error('该药品已由药房发放,请先执行退药处理,不可直接退回'); proxy.$message.error('该医嘱已发药,无法退回');
return; return;
} }
cancel(list).then((res) => { cancel(list).then((res) => {
@@ -318,16 +310,12 @@ function getSelectRows() {
// 获取选中的医嘱信息 // 获取选中的医嘱信息
let list = []; let list = [];
prescriptionList.value.forEach((item, index) => { prescriptionList.value.forEach((item, index) => {
const ref = proxy.$refs['tableRef' + index]; list = [...list, ...proxy.$refs['tableRef' + index][0].getSelectionRows()];
if (ref && ref[0]) {
list = [...list, ...ref[0].getSelectionRows()];
}
}); });
return list.map((item) => { return list.map((item) => {
return { return {
requestId: item.requestId, requestId: item.requestId,
requestTable: item.adviceTable, requestTable: item.adviceTable,
dispenseStatus: item.dispenseStatus,
}; };
}); });
} }

View File

@@ -546,7 +546,7 @@
v-model="form.admissionTime" v-model="form.admissionTime"
type="datetime" type="datetime"
placeholder="选择入室时间" placeholder="选择入室时间"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DDTHH:mm:ss"
format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm"
style="width: 100%" style="width: 100%"
/> />
@@ -558,7 +558,7 @@
v-model="form.entryTime" v-model="form.entryTime"
type="datetime" type="datetime"
placeholder="选择进室时间" placeholder="选择进室时间"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DDTHH:mm:ss"
format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm"
style="width: 100%" style="width: 100%"
/> />
@@ -570,7 +570,7 @@
v-model="form.anesStart" v-model="form.anesStart"
type="datetime" type="datetime"
placeholder="选择麻醉开始时间" placeholder="选择麻醉开始时间"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DDTHH:mm:ss"
format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm"
style="width: 100%" style="width: 100%"
/> />
@@ -584,7 +584,7 @@
v-model="form.startTime" v-model="form.startTime"
type="datetime" type="datetime"
placeholder="选择切开时间" placeholder="选择切开时间"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DDTHH:mm:ss"
format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm"
style="width: 100%" style="width: 100%"
/> />
@@ -596,7 +596,7 @@
v-model="form.endTime" v-model="form.endTime"
type="datetime" type="datetime"
placeholder="选择手术结束时间" placeholder="选择手术结束时间"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DDTHH:mm:ss"
format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm"
style="width: 100%" style="width: 100%"
/> />
@@ -608,7 +608,7 @@
v-model="form.anesEnd" v-model="form.anesEnd"
type="datetime" type="datetime"
placeholder="选择麻醉结束时间" placeholder="选择麻醉结束时间"
value-format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DDTHH:mm:ss"
format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm"
style="width: 100%" style="width: 100%"
/> />
@@ -689,7 +689,7 @@
</el-dialog> </el-dialog>
<!-- 手术申请查询弹窗 --> <!-- 手术申请查询弹窗 -->
<el-dialog :title="'手术申请查询'" v-model="showApplyDialog" width="1200px" @close="cancelApplyDialog" class="surgery-apply-dialog"> <el-dialog :title="'手术申请查询'" v-model="showApplyDialog" width="1200px" @close="cancelApplyDialog">
<!-- 查询条件区 --> <!-- 查询条件区 -->
<el-form :model="applyQueryParams" ref="applyQueryRef" :inline="true" class="query-form"> <el-form :model="applyQueryParams" ref="applyQueryRef" :inline="true" class="query-form">
<el-form-item label="手术单号" prop="surgeryNo"> <el-form-item label="手术单号" prop="surgeryNo">
@@ -749,8 +749,8 @@
@row-click="handleApplyRowClick" @row-click="handleApplyRowClick"
:row-class-name="tableRowClassName" :row-class-name="tableRowClassName"
style="width: 100%" style="width: 100%"
max-height="340" max-height="400"
:scroll="{ y: 340 }" :scroll="{ y: 400 }"
> >
<el-table-column type="selection" width="55" :selectable="handleSelectable" /> <el-table-column type="selection" width="55" :selectable="handleSelectable" />
<el-table-column label="ID" align="center" width="80" fixed> <el-table-column label="ID" align="center" width="80" fixed>
@@ -781,7 +781,7 @@
</el-table> </el-table>
<!-- 底部分页区 --> <!-- 底部分页区 -->
<div class="pagination-container apply-pagination" style="margin-top: 10px; padding-bottom: 20px"> <div class="pagination-container" style="margin-top: 10px; padding-bottom: 10px">
<pagination <pagination
v-show="applyTotal > 0" v-show="applyTotal > 0"
:total="applyTotal" :total="applyTotal"
@@ -792,12 +792,10 @@
@pagination="getSurgicalScheduleList" @pagination="getSurgicalScheduleList"
/> />
</div> </div>
<!-- 分页与底部操作区之间的间隔 -->
<div style="height: 48px"></div>
<!-- 底部操作区 --> <!-- 底部操作区 -->
<template #footer> <template #footer>
<div class="dialog-footer" style="margin-top: 24px; padding-top: 12px; border-top: 1px solid #ebeef5"> <div class="dialog-footer">
<el-button @click="cancelApplyDialog">取消</el-button> <el-button @click="cancelApplyDialog">取消</el-button>
<el-button type="primary" @click="confirmApply">确认</el-button> <el-button type="primary" @click="confirmApply">确认</el-button>
</div> </div>
@@ -832,7 +830,6 @@
</div> </div>
<div style="padding: 10px"> <div style="padding: 10px">
<prescriptionlist :patientInfo="chargePatientInfo" ref="prescriptionRef" <prescriptionlist :patientInfo="chargePatientInfo" ref="prescriptionRef"
:generateSourceEnum="1"
:sourceBillNo="chargePatientInfo.sourceBillNo" /> :sourceBillNo="chargePatientInfo.sourceBillNo" />
<div class="overlay" v-if="disabled"></div> <div class="overlay" v-if="disabled"></div>
</div> </div>
@@ -2352,22 +2349,6 @@ function getRowClassName({ row, rowIndex }) {
margin-left: 10px; margin-left: 10px;
} }
/* 手术申请查询弹窗 — 分页与footer间距 */
.surgery-apply-dialog :deep(.el-dialog__body) {
padding-bottom: 32px;
}
.surgery-apply-dialog :deep(.el-dialog__footer) {
padding-top: 0;
}
.surgery-apply-dialog :deep(.apply-pagination) {
padding-bottom: 24px;
margin-bottom: 16px;
border-bottom: 1px solid #ebeef5;
}
.surgery-apply-dialog :deep(.apply-pagination .el-pagination) {
margin-right: 80px;
}
/* 选中行样式 */ /* 选中行样式 */
:deep(.el-table .selected-row) { :deep(.el-table .selected-row) {
background-color: #ecf5ff !important; background-color: #ecf5ff !important;
@@ -2378,21 +2359,3 @@ function getRowClassName({ row, rowIndex }) {
} }
</style> </style>
<style>
/* 手术申请查询弹窗 — 非 scoped 确保穿透 teleport */
.surgery-apply-dialog .apply-pagination {
padding-bottom: 24px !important;
margin-bottom: 16px !important;
border-bottom: 1px solid #ebeef5 !important;
}
.surgery-apply-dialog .apply-pagination .el-pagination {
margin-right: 80px !important;
}
.surgery-apply-dialog .el-dialog__body {
padding-bottom: 32px !important;
}
.surgery-apply-dialog .el-dialog__footer {
padding-top: 0 !important;
}
</style>