Compare commits

...

7 Commits

Author SHA1 Message Date
赵云
375108fa05 Fix Bug #455: 门诊医生站-医嘱:开立诊疗医嘱时执行科室默认获取逻辑有误且显示为原始ID
根因修复:
1. 默认科室逻辑错误:row.orgId来自wor_activity_definition.org_id(项目所属科室),不是执行科室。
   改为优先使用positionId(adm_organization_location配置的执行科室),回退到患者就诊科室。
2. 显示为原始ID:getOrgList()为异步调用但未await,导致findOrgNameById执行时organization树未加载。
   将setValue改为async函数并await getOrgList(),确保科室名称解析时数据已就绪。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 18:39:13 +08:00
华佗
b21bdc2043 Fix Bug #469: [住院医生工作站-检验申请] 完善【操作】列临床业务逻辑:支持按状态动态切换修改、删除、撤回等功能
根据单据状态动态显示操作按钮:
- 待签发(状态0):显示修改、删除、详情
- 已签发(状态1):显示撤回、详情
- 其他状态:仅显示详情

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 18:39:13 +08:00
Ranyunqiao
116734f1f8 413 460 513 514 2026-05-12 18:39:13 +08:00
赵云
288f2133ca Fix Bug #470: 住院医生工作站-手术申请单加载手术项目耗时过长,影响医生开单效率
根因:getAdviceBaseInfo 后端接口在查询手术项目时,仍会执行与手术无关的库存查询
(getAdviceInventory)、全表扫描待发放记录(getAdviceDraftInventory)以及药房科室
配置查询(getMedLocationConfig),其中 getAdviceDraftInventory 对
med_medication_dispense 和 wor_device_dispense 做全表扫描,无任何过滤条件,
导致手术/诊疗场景下的额外数据库开销。

修复:在 DoctorStationAdviceAppServiceImpl.getAdviceBaseInfo() 中增加类型判断,
当 adviceTypes 不包含药品(1)或耗材(2)时跳过所有库存相关查询,因为这些查询对手术/
诊疗(3,6)项目无意义,且下游代码仅在药品/耗材处理分支中使用这些变量。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 17:22:13 +08:00
赵云
d60eedf6a8 Fix Bug #400: 门诊医生站点击【完诊】后,triage_queue_item 表 status 字段未按规范更新为 30
完诊API后端要求同时传递 encounterId 和 firstEnum 两个参数:
1. DoctorCallDialog.vue:已有修复只传了 encounterId,缺少 firstEnum,导致后端校验失败
2. patientList.vue:仍传递原始值而非对象,且同样缺少 firstEnum

修复:两处调用均改为传递 { encounterId, firstEnum } 对象,firstEnum 默认值为1

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 17:05:17 +08:00
赵云
15daf26f6d Fix Bug #400: 门诊医生站点击【完诊】后,triage_queue_item 表 status 字段未按规范更新为 30
队列弹窗【完成】按钮调用完诊API时,传递了原始 Long 值而非对象参数,
导致后端 @RequestBody 反序列化时 encounterId 为 null,队列项状态无法更新。

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 13:05:59 +08:00
赵云
b05dcab1e3 Fix Bug #492: 【门诊手术安排】关闭"手术计费"主弹窗后,项目字典选择列表依然残留悬浮在界面上
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 12:31:06 +08:00
15 changed files with 297 additions and 167 deletions

View File

@@ -231,29 +231,49 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
// 费用定价主表ID集合
List<Long> chargeItemDefinitionIdList = adviceBaseDtoList.stream().map(AdviceBaseDto::getChargeItemDefinitionId)
.collect(Collectors.toList());
// 医嘱库存集合
List<AdviceInventoryDto> adviceInventoryList = doctorStationAdviceAppMapper.getAdviceInventory(locationId,
adviceDefinitionIdList,
CommonConstants.SqlCondition.ABOUT_INVENTORY_TABLE_STR, PublicationStatus.ACTIVE.getValue());
// 待发放个数信息
List<AdviceInventoryDto> adviceDraftInventoryList = doctorStationAdviceAppMapper.getAdviceDraftInventory(
CommonConstants.TableName.MED_MEDICATION_DEFINITION, CommonConstants.TableName.ADM_DEVICE_DEFINITION,
DispenseStatus.DRAFT.getValue(), DispenseStatus.PREPARATION.getValue());
// 预减库存
List<AdviceInventoryDto> adviceInventory = adviceUtils.subtractInventory(adviceInventoryList,
adviceDraftInventoryList);
// 查询取药科室配置
List<AdviceInventoryDto> medLocationConfig = doctorStationAdviceAppMapper.getMedLocationConfig(organizationId);
// 将配置转为 {categoryCode -> 允许的locationId集合}
Map<String, Set<Long>> allowedLocByCategory = new HashMap<>();
if (medLocationConfig != null && !medLocationConfig.isEmpty()) {
for (AdviceInventoryDto cfg : medLocationConfig) {
if (cfg.getCategoryCode() == null || cfg.getLocationId() == null) {
continue;
// 判断是否包含药品或耗材类型(只有这些类型才需要库存相关查询)
boolean hasMedOrDevice = adviceTypes != null
&& (adviceTypes.contains(1) || adviceTypes.contains(2));
// 医嘱库存集合 — 仅药品/耗材需要库存查询,手术/诊疗(3,6)无库存概念,跳过以减少数据库开销
List<AdviceInventoryDto> adviceInventoryList;
List<AdviceInventoryDto> adviceDraftInventoryList;
List<AdviceInventoryDto> adviceInventory;
if (hasMedOrDevice) {
adviceInventoryList = doctorStationAdviceAppMapper.getAdviceInventory(locationId,
adviceDefinitionIdList,
CommonConstants.SqlCondition.ABOUT_INVENTORY_TABLE_STR, PublicationStatus.ACTIVE.getValue());
// 待发放个数信息
adviceDraftInventoryList = doctorStationAdviceAppMapper.getAdviceDraftInventory(
CommonConstants.TableName.MED_MEDICATION_DEFINITION, CommonConstants.TableName.ADM_DEVICE_DEFINITION,
DispenseStatus.DRAFT.getValue(), DispenseStatus.PREPARATION.getValue());
// 预减库存
adviceInventory = adviceUtils.subtractInventory(adviceInventoryList, adviceDraftInventoryList);
} else {
adviceInventoryList = Collections.emptyList();
adviceDraftInventoryList = Collections.emptyList();
adviceInventory = Collections.emptyList();
}
// 查询取药科室配置 — 仅药品开单场景需要
List<AdviceInventoryDto> medLocationConfig;
Map<String, Set<Long>> allowedLocByCategory;
if (hasMedOrDevice) {
medLocationConfig = doctorStationAdviceAppMapper.getMedLocationConfig(organizationId);
// 将配置转为 {categoryCode -> 允许的locationId集合}
allowedLocByCategory = new HashMap<>();
if (medLocationConfig != null && !medLocationConfig.isEmpty()) {
for (AdviceInventoryDto cfg : medLocationConfig) {
if (cfg.getCategoryCode() == null || cfg.getLocationId() == null) {
continue;
}
allowedLocByCategory.computeIfAbsent(String.valueOf(cfg.getCategoryCode()), k -> new HashSet<>())
.add(cfg.getLocationId());
}
allowedLocByCategory.computeIfAbsent(String.valueOf(cfg.getCategoryCode()), k -> new HashSet<>())
.add(cfg.getLocationId());
}
} else {
medLocationConfig = Collections.emptyList();
allowedLocByCategory = Collections.emptyMap();
}
// 费用定价子表信息 - 使用分批处理避免大量参数问题
List<AdvicePriceDto> childCharge = new ArrayList<>();

View File

@@ -1,5 +1,6 @@
package com.openhis.web.doctorstation.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
@@ -55,6 +56,7 @@ public class InfectiousDiseaseReportDto {
private String sex;
/** 出生日期 */
@JsonFormat(pattern = "yyyy-MM-dd")
private Date birthday;
/** 实足年龄 */

View File

@@ -17,6 +17,9 @@ import com.openhis.common.constant.PromptMsgConstant;
import com.openhis.common.enums.*;
import com.openhis.common.utils.EnumUtils;
import com.openhis.common.utils.HisQueryUtils;
import com.core.common.utils.SecurityUtils;
import com.openhis.workflow.domain.InventoryItem;
import com.openhis.workflow.service.IInventoryItemService;
import com.openhis.web.basedatamanage.dto.LocationDto;
import com.openhis.web.common.dto.UnitDto;
import com.openhis.web.inventorymanage.appservice.IProductTransferAppService;
@@ -57,6 +60,9 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService
@Autowired
private IPractitionerService practitionerService;
@Autowired
private IInventoryItemService inventoryItemService;
/**
* 商品调拨页面初始化
*
@@ -196,6 +202,23 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService
@Override
public R<?> addOrEditBatchTransferReceipt(List<ProductTransferDto> productTransferDtoList, Boolean flag) {
// 校验调拨数量:必须 > 0 且不超过源库存数量(从数据库查实时库存)
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
for (ProductTransferDto dto : productTransferDtoList) {
if (dto.getItemQuantity() == null || dto.getItemQuantity().compareTo(java.math.BigDecimal.ZERO) <= 0) {
return R.fail("调拨数量必须大于0");
}
// 查询该药品在源仓库的实时库存总量
List<InventoryItem> inventoryList = inventoryItemService.selectInventoryByItemId(
dto.getItemId(), dto.getLotNumber(), dto.getSourceLocationId(), tenantId);
java.math.BigDecimal actualStock = inventoryList.stream()
.map(InventoryItem::getQuantity)
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
if (dto.getItemQuantity().compareTo(actualStock) > 0) {
return R.fail("调拨数量不可超出源库存数量(当前库存:" + actualStock + "");
}
}
List<String> idList = new ArrayList<>();
if (flag) {
// 批量保存按钮
@@ -309,6 +332,22 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService
@Override
public R<?> addOrEditTransferReceipt(List<ProductTransferDto> productTransferDtoList) {
// 校验调拨数量:必须 > 0 且不超过源库存数量(从数据库查实时库存)
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
for (ProductTransferDto dto : productTransferDtoList) {
if (dto.getItemQuantity() == null || dto.getItemQuantity().compareTo(java.math.BigDecimal.ZERO) <= 0) {
return R.fail("调拨数量必须大于0");
}
List<InventoryItem> inventoryList = inventoryItemService.selectInventoryByItemId(
dto.getItemId(), dto.getLotNumber(), dto.getSourceLocationId(), tenantId);
java.math.BigDecimal actualStock = inventoryList.stream()
.map(InventoryItem::getQuantity)
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
if (dto.getItemQuantity().compareTo(actualStock) > 0) {
return R.fail("调拨数量不可超出源库存数量(当前库存:" + actualStock + "");
}
}
List<String> idList = new ArrayList<>();
// 单据号取得
@@ -380,6 +419,25 @@ public class ProductTransferAppServiceImpl implements IProductTransferAppService
*/
@Override
public R<?> submitApproval(String busNo) {
// 提交前再次校验调拨数量(从数据库查实时库存)
List<SupplyRequest> requestList = supplyRequestService.getSupplyByBusNo(busNo);
if (requestList != null && !requestList.isEmpty()) {
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
for (SupplyRequest request : requestList) {
if (request.getItemQuantity() == null || request.getItemQuantity().compareTo(java.math.BigDecimal.ZERO) <= 0) {
return R.fail("调拨数量必须大于0请检查后重新保存");
}
// 查询该药品在源仓库的实时库存总量
List<InventoryItem> inventoryList = inventoryItemService.selectInventoryByItemId(
request.getItemId(), request.getLotNumber(), request.getSourceLocationId(), tenantId);
java.math.BigDecimal actualStock = inventoryList.stream()
.map(InventoryItem::getQuantity)
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add);
if (request.getItemQuantity().compareTo(actualStock) > 0) {
return R.fail("调拨数量不可超出源库存数量(当前库存:" + actualStock + "),请检查后重新保存");
}
}
}
// 单据提交审核
boolean result = supplyRequestService.submitApproval(busNo);
return result ? R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, null))

View File

@@ -519,9 +519,10 @@
>
<template #append>
<!-- 审核记录查看/审核都展示保证留痕可追溯 -->
<!-- 修复查看模式始终显示区块审核模式始终显示区块header + 空状态 timeline -->
<div
class="audit-records-section"
v-if="drawerMode === 'view' || (drawerMode === 'audit' && auditRecords.length > 0)"
v-if="drawerMode === 'view' || drawerMode === 'audit'"
>
<h3 class="section-title">审核记录</h3>
<el-timeline v-if="auditRecords.length > 0">

View File

@@ -375,7 +375,7 @@ const finishCallPatient = async () => {
return;
}
try {
await completeEncounter(currentCallPatient.value.encounterId);
await completeEncounter({ encounterId: currentCallPatient.value.encounterId, firstEnum: currentCallPatient.value.firstEnum || 1 });
emit('finish');
emit('update:dialogVisible', false);
ElMessage.success('患者已完诊');

View File

@@ -349,11 +349,17 @@ async function getList() {
if (res.code == 200) {
// 过滤掉中医诊断,只保留西医诊断
form.value.diagnosisList = res.data.filter(item => item.typeName !== '中医诊断');
// 为旧数据添加默认分类
// 为旧数据添加默认分类和selectedDiseases
form.value.diagnosisList.forEach(item => {
if (!item.classification) {
item.classification = '西医';
}
// 如果ybNo诊断编码符合传染病编码格式添加到selectedDiseases
if (item.ybNo && /^(01|02|03)/.test(item.ybNo)) {
item.selectedDiseases = [item.ybNo];
} else {
item.selectedDiseases = item.selectedDiseases || [];
}
});
emits('diagnosisSave', false);
}
@@ -685,23 +691,78 @@ async function handleFoodDiseasesCheck() {
/**
* 传染病报告卡处理
* 通过诊断目录维护的'报卡类型'字段自动识别是否有需要填写的传染病报告卡
* 如果有则弹出诊断对应需登记的报告卡界面
* 通过诊断名称自动识别并勾选传染病报告卡中的疾病
*/
function handleInfectiousDiseaseReport() {
// 查找所有有报卡类型的诊断reportTypeCode不为空
const diagnosesWithReportType = form.value.diagnosisList.filter(d => d.reportTypeCode);
// 疾病名称到报卡编码的映射(根据传染病报告卡弹窗中的疾病列表
const diseaseNameToCode = {
// 甲类
'鼠疫': '0101',
'霍乱': '0102',
// 乙类
'传染性非典型肺炎': '0201',
'艾滋病': '0202',
'病毒性肝炎': '0203',
'脊髓灰质炎': '0204',
'人感染高致病性禽流感': '0205',
'麻疹': '0206',
'流行性出血热': '0207',
'狂犬病': '0208',
'流行性乙型脑炎': '0209',
'登革热': '0210',
'炭疽': '0211',
'细菌性和阿米巴性痢疾': '0212',
'肺结核': '0213',
'伤寒和副伤寒': '0214',
'流行性脑脊髓膜炎': '0215',
'百日咳': '0216',
'白喉': '0217',
'新生儿破伤风': '0218',
'猩红热': '0219',
'布鲁氏菌病': '0220',
'淋病': '0221',
'梅毒': '0222',
'钩端螺旋体病': '0223',
'血吸虫病': '0224',
'疟疾': '0225',
'新型冠状病毒肺炎': '0226',
'甲型H1N1流感': '0227',
'人感染H7N9禽流感': '0228',
// 丙类
'流行性感冒': '0301',
'流行性腮腺炎': '0302',
'风疹': '0303',
'急性出血性结膜炎': '0304',
'麻风病': '0305',
'流行性和地方性斑疹伤寒': '0306',
'黑热病': '0307',
'包虫病': '0308',
'丝虫病': '0309',
'除霍乱/菌痢/伤寒副伤寒以外的感染性腹泻病': '0310',
'其它感染性腹泻病': '0310',
'手足口病': '0311',
};
if (diagnosesWithReportType.length === 0) {
// 获取所有诊断名称对应的报卡编码
const allSelectedDiseases = form.value.diagnosisList
.map(d => diseaseNameToCode[d.name] || null)
.filter(code => code);
if (allSelectedDiseases.length === 0) {
return;
}
// 优先使用主诊断,如果没有主诊断有报卡类型则使用第一个有报卡类型的诊断
const mainDiagnosisWithReport = diagnosesWithReportType.find(d => d.maindiseFlag === 1);
const targetDiagnosis = mainDiagnosisWithReport || diagnosesWithReportType[0];
// 优先使用主诊断
const mainDiagnosis = form.value.diagnosisList.find(d => d.maindiseFlag === 1);
const firstDiagnosis = form.value.diagnosisList[0];
const diagnosisToShow = {
...(mainDiagnosis || firstDiagnosis),
selectedDiseases: allSelectedDiseases
};
// 弹出传染病报告卡弹窗
proxy.$refs.infectiousDiseaseReportRef?.show(targetDiagnosis);
proxy.$refs.infectiousDiseaseReportRef?.show(diagnosisToShow);
}
/**
@@ -814,7 +875,8 @@ form.value.diagnosisList.push({
classification: '西医', // 默认为西医
onsetDate: getCurrentDate(),
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: getCurrentDate()
diagnosisTime: getCurrentDate(),
selectedDiseases: data.ybNo ? [data.ybNo] : [], // 用于传染病报告卡自动勾选
});
// 添加后按排序号排序

View File

@@ -16,15 +16,15 @@
v-model="form.cardNo"
class="card-number-input"
placeholder="单位自编,与网络直报一致"
maxlength="12"
:disabled="readOnly"
maxlength="20"
:disabled="readOnly || dialogReadOnly"
/>
</el-space>
</el-card>
</template>
<el-card class="report-form" shadow="never">
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" :disabled="readOnly">
<el-form ref="formRef" :model="form" :rules="rules" label-position="top" :disabled="readOnly || dialogReadOnly">
<!-- 患者姓名家长姓名身份证号 -->
<el-row :gutter="16" class="form-row">
<el-col :span="8" class="form-item">
@@ -476,9 +476,9 @@
<template #footer>
<slot name="footer" :close="handleClose" :submit-loading="submitLoading">
<el-space :size="16" justify="center" class="dialog-footer-space" style="display: flex; justify-content: center; width: 100%;">
<el-button v-if="!readOnly" type="primary" @click="handleSubmit" :loading="submitLoading" class="blue-button">保 存</el-button>
<el-button v-if="!(readOnly || dialogReadOnly)" type="primary" @click="handleSubmit" :loading="submitLoading" class="blue-button">保 存</el-button>
<el-button type="info" @click="handleClose">关 闭</el-button>
<el-button v-if="!readOnly" type="danger" @click="handleReset"> </el-button>
<el-button v-if="!(readOnly || dialogReadOnly)" type="danger" @click="handleReset"> </el-button>
</el-space>
</slot>
</template>
@@ -510,6 +510,7 @@ const DISEASE_NAMES = {
};
const dialogVisible = ref(false);
const dialogReadOnly = ref(false);
const formRef = ref(null);
// 保存按钮加载状态,防止重复提交
const submitLoading = ref(false);
@@ -1037,8 +1038,9 @@ function resetAddressSelector() {
* 以只读详情方式打开报卡弹窗,供报卡管理等页面复用医生站报卡样式。
* @param {Object} reportData - 报卡详情数据
*/
function showReport(reportData = {}) {
function showReport(reportData = {}, readOnly = true) {
dialogVisible.value = true;
dialogReadOnly.value = readOnly;
resetAddressSelector();
initProvinceOptions();
@@ -1205,6 +1207,7 @@ function calculateAge() {
*/
async function show(diagnosisData) {
dialogVisible.value = true;
dialogReadOnly.value = false;
// 重置地址选择器状态
resetAddressSelector();
@@ -1238,14 +1241,13 @@ async function show(diagnosisData) {
let cardNo = '';
try {
const res = await getNextCardNo(orgCode);
if (res.code === 200 && res.data) {
if (res.code === 200 && res.data && res.data.length >= 12) {
cardNo = res.data;
} else {
cardNo = 'TEMP_' + Date.now();
}
// API失败或返回不合规时保持为空字符串由用户手动填写或后端自动生成
} catch (err) {
console.error('获取卡片编号失败:', err);
cardNo = 'TEMP_' + Date.now();
// 保持为空,不使用不合规的临时值
}
form.value = {
@@ -1424,9 +1426,9 @@ async function buildSubmitData() {
function validateFormManually() {
const errors = [];
// 卡片编号验证(可选但如果填写了必须是12位
if (form.value.cardNo && form.value.cardNo.length !== 12) {
errors.push('卡片编号必须为12位');
// 卡片编号验证(至少12位后端自动生成16位编号
if (form.value.cardNo && form.value.cardNo.length < 12) {
errors.push('卡片编号至少12位');
}
// 身份证号验证

View File

@@ -109,6 +109,7 @@ const props = defineProps({
});
const { proxy } = getCurrentInstance();
const encounterId = ref();
const firstEnum = ref(1); // 初复诊标识1=初诊2=复诊
onMounted(() => {
getPatientList();
});
@@ -127,6 +128,7 @@ function getPatientList() {
function clickRow(row) {
encounterId.value = row.encounterId;
firstEnum.value = row.firstEnum ?? row.first_enum ?? 1;
emits('cellClick', row);
}
@@ -182,7 +184,7 @@ function handleComplete() {
}
proxy.$modal.confirm('是否完成该患者问诊?').then(() => {
proxy.$modal.loading();
completeEncounter(encounterId.value).then(() => {
completeEncounter({ encounterId: encounterId.value, firstEnum: firstEnum.value }).then(() => {
proxy.$modal.closeLoading();
proxy.$modal.msgSuccess('完成问诊成功');
getPatientList();

View File

@@ -2495,11 +2495,13 @@ function handleSave(prescriptionId) {
// 🔧 BugFix#318: 从 parsedContent 提取标准医嘱字段,排除手术特有字段
const standardFields = [
'accountId', 'chargeItemId', 'conditionDefinitionId', 'conditionId',
'contentJson', 'definitionDetailId', 'definitionId', 'diagnosisName',
'dosageInstruction', 'effectiveOrgId', 'encounterDiagnosisId',
'encounterId', 'lotNumber', 'patientId', 'practitionerId',
'prescriptionNo', 'skinTestFlag', 'unitPrice', 'volume', 'ybClassEnum'
'accountId', 'chargeItemId', 'conditionDefinitionId', 'conditionId',
'contentJson', 'definitionDetailId', 'definitionId', 'diagnosisName',
'dosageInstruction', 'effectiveOrgId', 'encounterDiagnosisId',
'encounterId', 'lotNumber', 'patientId', 'practitionerId',
'prescriptionNo', 'skinTestFlag', 'unitPrice', 'volume', 'ybClassEnum',
// 🔧 Bug Fix: 添加 therapyEnum 字段医嘱类型1=长期, 2=临时)
'therapyEnum'
];
let filteredContent = {};
standardFields.forEach(field => {
@@ -3143,7 +3145,9 @@ function handleSaveBatch(prescriptionId) {
// 🔧 Bug Fix: 添加 definitionId 和 definitionDetailId 字段
'definitionId', 'definitionDetailId',
// 🔧 Bug Fix: 添加 categoryEnum 字段(耗材必填)
'categoryEnum'
'categoryEnum',
// 🔧 Bug Fix: 添加 therapyEnum 字段医嘱类型1=长期, 2=临时)
'therapyEnum'
];
let filteredItem = {};
standardItemFields.forEach(field => {
@@ -3342,7 +3346,7 @@ function syncGroupFields(row) {
}
}
function setValue(row) {
async function setValue(row) {
unitCodeList.value = [];
unitCodeList.value.push({ value: row.unitCode, label: row.unitCode_dictText, type: 'unit' });
@@ -3394,8 +3398,8 @@ function setValue(row) {
showPopover: false, // 确保查询框关闭
};
console.log('[BugFix] setValue - prescriptionList[rowIndex].adviceType_dictText:', prescriptionList.value[rowIndex.value].adviceType_dictText);
// 🔧 修复执行科室逻辑:优先使用项目维护的所属科室(row.orgId)其次使用positionId最后回退到患者科室
prescriptionList.value[rowIndex.value].orgId = row.orgId || row.positionId || props.patientInfo?.orgId;
// 🔧 Bug #455: 执行科室默认逻辑:使用positionId(诊疗执行科室配置) → 患者就诊科室不再使用row.orgId(项目所属科室)
prescriptionList.value[rowIndex.value].orgId = row.positionId || props.patientInfo?.orgId;
prescriptionList.value[rowIndex.value].dose = row.dose || row.doseQuantity;
prescriptionList.value[rowIndex.value].quantity = row.quantity || 1;
prescriptionList.value[rowIndex.value].unitCodeList = unitCodeList.value;
@@ -3514,7 +3518,7 @@ function setValue(row) {
prescriptionList.value[rowIndex.value].positionId = finalLocationId;
}
} else {
getOrgList();
await getOrgList();
// 会诊类型adviceType == 5和诊疗类型adviceType == 3的处理
if (row.adviceType == 5) {
// 会诊类型:设置默认值

View File

@@ -151,23 +151,12 @@
</div>
</div>
<el-dialog
v-model="detailVisible"
:title="detailMode === 'view' ? '报卡详情 - 中华人民共和国传染病报告卡' : '编辑报卡 - 中华人民共和国传染病报告卡'"
width="1100px"
destroy-on-close
class="card-detail-dialog"
>
<InfectiousReport
:mode=" detailMode"
:card-data="currentCard"
@submit-edit="handleSaveEdit"
style="max-height: 75vh; overflow-y: auto;"
/>
<template #footer>
<el-button @click="detailVisible = false">关闭</el-button>
</template>
</el-dialog>
<InfectiousDiseaseReportDialog
ref="infectiousDiseaseReportRef"
:read-only="detailMode === 'view'"
@success="detailVisible = false"
@close="detailVisible = false"
/>
</div>
</template>
@@ -175,7 +164,7 @@
import { ref, reactive, onMounted, onActivated, onBeforeUnmount, nextTick, computed } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { DataAnalysis, Warning, CircleCheck, Check, RefreshRight } from '@element-plus/icons-vue';
import InfectiousReport from '../components/infectiousReport/index.vue';
import InfectiousDiseaseReportDialog from '../components/diagnosis/infectiousDiseaseReportDialog.vue';
import {
getDoctorCardStatistics,
getDoctorCardList,
@@ -185,7 +174,6 @@ import {
batchDeleteCards,
exportCardToWord,
getCardDetail,
updateDoctorCard,
} from './api';
const loading = ref(false);
@@ -211,7 +199,7 @@ const queryParams = reactive({
const detailVisible = ref(false);
const detailMode = ref('view');
const currentCard = ref({});
const infectiousDiseaseReportRef = ref(null);
// 计算表格高度:根据视口高度动态调整
const tableHeight = computed(() => {
@@ -327,9 +315,11 @@ async function handleView(row) {
try {
const res = await getCardDetail(row.cardNo);
if (res.code === 200) {
currentCard.value = res.data || {};
detailMode.value = 'view';
detailVisible.value = true;
nextTick(() => {
infectiousDiseaseReportRef.value?.showReport(res.data || {});
});
}
} catch (error) {
ElMessage.error('获取详情失败');
@@ -340,57 +330,17 @@ async function handleEdit(row) {
try {
const res = await getCardDetail(row.cardNo);
if (res.code === 200) {
currentCard.value = res.data || {};
detailMode.value = 'edit';
detailVisible.value = true;
nextTick(() => {
infectiousDiseaseReportRef.value?.showReport(res.data || {}, false);
});
}
} catch (error) {
ElMessage.error('获取详情失败');
}
}
async function handleSaveEdit(submitData) {
// submitData 来自 InfectiousReport 组件的 submit-edit 事件
try {
const updateData = {
cardNo: submitData.cardNo,
phone: submitData.phone,
contactPhone: submitData.contactPhone,
onsetDate: submitData.onsetDate,
diagDate: submitData.diagDate,
diseaseCode: submitData.diseaseCode,
diseaseType: submitData.diseaseType,
otherDisease: submitData.otherDisease,
caseClass: submitData.caseClass,
occupation: submitData.occupation,
patientBelong: submitData.patientBelong,
addressProv: submitData.addressProv,
addressCity: submitData.addressCity,
addressCounty: submitData.addressCounty,
addressTown: submitData.addressTown,
addressVillage: submitData.addressVillage,
addressHouse: submitData.addressHouse,
workplace: submitData.workplace,
parentName: submitData.parentName,
deathDate: submitData.deathDate,
correctName: submitData.correctName,
withdrawReason: submitData.withdrawReason,
remark: submitData.remark,
};
const res = await updateDoctorCard(updateData);
if (res.code === 200) {
ElMessage.success('保存成功');
detailVisible.value = false;
getList();
} else {
ElMessage.error(res.msg || '保存失败');
}
} catch (error) {
ElMessage.error('保存失败:' + (error.message || '网络错误'));
}
}
async function handleSubmit(row) {
try {
await ElMessageBox.confirm('确认提交该报卡?', '提示', {
@@ -800,17 +750,4 @@ function handleResize() {
margin-left: 0;
}
}
/* 报卡详情弹窗 */
:deep(.card-detail-dialog .el-dialog__body) {
padding: 0;
overflow: hidden;
}
:deep(.card-detail-dialog .infectious-report-container) {
padding: 16px;
height: auto;
max-height: 70vh;
overflow-y: auto;
}
</style>

View File

@@ -103,8 +103,15 @@
</template>
</el-table-column>
<el-table-column prop="requesterId_dictText" label="申请者" width="120" />
<el-table-column label="操作" align="center" fixed="right">
<el-table-column label="操作" align="center" fixed="right" width="160">
<template #default="scope">
<template v-if="scope.row.billStatus == 0 || scope.row.status == 0">
<el-button link type="primary" @click="handleEdit(scope.row)">修改</el-button>
<el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
<template v-else-if="scope.row.billStatus == 1 || scope.row.status == 1">
<el-button link type="warning" @click="handleWithdraw(scope.row)">撤回</el-button>
</template>
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
</template>
</el-table-column>
@@ -181,7 +188,7 @@
import {computed, getCurrentInstance, ref, watch} from 'vue';
import {Refresh, Search} from '@element-plus/icons-vue';
import {patientInfo} from '../../store/patient.js';
import {getInspection} from './api';
import {getInspection, deleteRequestForm, withdrawRequestForm} from './api';
import {getOrgList} from '@/views/doctorstation/components/api.js';
const { proxy } = getCurrentInstance();
@@ -399,6 +406,61 @@ const handleViewDetail = async (row) => {
detailDialogVisible.value = true;
};
/**
* 修改检验申请单(待签发状态)
*/
const handleEdit = (row) => {
// 复用详情查看逻辑,后续可扩展为打开编辑弹窗
handleViewDetail(row);
proxy.$modal?.msgInfo?.('修改功能待接入,请通过详情弹窗查看后重新开立');
};
/**
* 删除检验申请单(仅待签发状态可删除)
*/
const handleDelete = async (row) => {
try {
await proxy.$modal?.confirm?.(`确定要删除申请单 "${row.prescriptionNo}" 吗?此操作不可恢复。`);
} catch {
return; // 用户取消
}
try {
const res = await deleteRequestForm({ prescriptionNo: row.prescriptionNo });
if (res?.code === 200) {
proxy.$modal?.msgSuccess?.('删除成功');
await fetchData();
} else {
proxy.$modal?.msgError?.(res?.msg || '删除失败');
}
} catch (e) {
proxy.$modal?.msgError?.(e.message || '删除异常');
}
};
/**
* 撤回检验申请单(已签发状态撤回至待签发)
*/
const handleWithdraw = async (row) => {
try {
await proxy.$modal?.confirm?.(`确定要撤回申请单 "${row.prescriptionNo}" 吗?撤回后将恢复为待签发状态。`);
} catch {
return; // 用户取消
}
try {
const res = await withdrawRequestForm({ prescriptionNo: row.prescriptionNo });
if (res?.code === 200) {
proxy.$modal?.msgSuccess?.('撤回成功');
await fetchData();
} else {
proxy.$modal?.msgError?.(res?.msg || '撤回失败');
}
} catch (e) {
proxy.$modal?.msgError?.(e.message || '撤回异常');
}
};
watch(
() => patientInfo.value?.encounterId,
async (val) => {

View File

@@ -11,9 +11,9 @@
>
<div>
<el-radio-group v-model="type" class="ml20" @change="handleRadioChange">
<el-radio :label="1">全部</el-radio>
<el-radio :label="2">长期</el-radio>
<el-radio :label="3">临时</el-radio>
<el-radio :label="null">全部</el-radio>
<el-radio :label="1">长期</el-radio>
<el-radio :label="2">临时</el-radio>
</el-radio-group>
<el-button class="ml20" type="primary" plain @click="handleGetPrescription">
查询
@@ -159,7 +159,7 @@ import {formatDateStr} from '@/utils/index';
const activeNames = ref([]);
const prescriptionList = ref([]);
const deadline = ref(formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59');
const type = ref(1);
const type = ref(null);
const { proxy } = getCurrentInstance();
const loading = ref(false);
const chooseAll = ref(false);
@@ -181,7 +181,7 @@ function handleGetPrescription() {
getPrescriptionList({
encounterIds: encounterIds,
requestStatus: props.requestStatus,
...(type.value !== undefined ? { therapyEnum: type.value } : {}),
...(type.value != null ? { therapyEnum: type.value } : {}),
pageSize: 10000,
pageNo: 1,
}).then((res) => {

View File

@@ -744,11 +744,7 @@ function handleSubmitApproval() {
} else {
submitApproval(receiptHeaderForm.busNo).then((response) => {
proxy.$modal.msgSuccess('提交审批成功');
// proxy.$tab.closePage(route).then(({ visitedViews }) => { // 关闭当前页
// toLastView(visitedViews, route)
// })
tagsViewStore.delView(router.currentRoute.value);
// 跳转到调拨管理页面
router.replace({ path: 'transferManagentList' });
});
}
@@ -871,7 +867,6 @@ function handleUnitCodeChange(row, index, value) {
}
function handleItemQuantityChange(row, value) {
debugger;
let quantityTemp = ''; // 转换成小单位的临时变量 做校验
// 大单位情况
if (row.unitCodeMap[row.unitCode] == 'unit') {
@@ -882,12 +877,6 @@ function handleItemQuantityChange(row, value) {
row.itemQuantity = value;
quantityTemp = value;
}
if (row.totalSourceQuantity < quantityTemp) {
proxy.$modal.msgWarning('调拨数量不可超出源库存数量');
row.itemQuantity = 0;
row.itemQuantityDisplay = 0;
return;
}
row.totalPrice = ((row.price * quantityTemp) / row.partPercent).toFixed(2);
// 更新总数据中对应的记录
const totalIndex = totalIncentoryInfoList.value.findIndex((item) => item.id === row.id);
@@ -918,11 +907,12 @@ function remakeBlur(row, index) {
editBatchTransfer(index);
}
function handleSave() {
const invalidRow = totalIncentoryInfoList.value.find(
// 校验单价
const invalidPriceRow = totalIncentoryInfoList.value.find(
(row) => !row.price || row.price <= 0
);
if (invalidRow) {
proxy.$message.warning('调拨单价不能为空或为0请检查');
if (invalidPriceRow) {
proxy.$modal.msgError('调拨单价不能为空或为0请检查');
return;
}
addTransferProductBatch(totalIncentoryInfoList.value).then((res) => {

View File

@@ -970,12 +970,6 @@ function handleItemQuantityChange(row, value) {
row.itemQuantity = value;
quantityTemp = value;
}
if (row.totalSourceQuantity < quantityTemp) {
proxy.$modal.msgWarning('调拨数量不可超出源库存数量');
row.itemQuantity = 0;
row.itemQuantityDisplay = 0;
return;
}
row.totalPrice = ((row.price * quantityTemp) / row.partPercent).toFixed(2);
}
@@ -1022,11 +1016,7 @@ function handleSubmitApproval() {
} else {
submitApproval(receiptHeaderForm.busNo).then((response) => {
proxy.$modal.msgSuccess('提交审批成功');
// proxy.$tab.closePage(route).then(({ visitedViews }) => { // 关闭当前页
// toLastView(visitedViews, route)
// })
tagsViewStore.delView(router.currentRoute.value);
// 跳转到调拨管理页面
router.replace({ path: 'transferManagentList' });
store.clearCurrentDataDB();
});

View File

@@ -829,7 +829,7 @@
</el-descriptions>
</div>
<div style="padding: 10px">
<prescriptionlist :patientInfo="chargePatientInfo" ref="prescriptionRef" />
<prescriptionlist v-if="showChargeDialog" :patientInfo="chargePatientInfo" ref="prescriptionRef" />
<div class="overlay" v-if="disabled"></div>
</div>
</div>