Bug #384: 检查方法联动功能完善,增加套餐价格查询和项目卡片展开选择
Bug #386: 检验申请删除时同步删除关联收费项目 Bug #382: 选择项目后保持当前页签状态 Bug #380,381: 临床诊断获取主诊断字段名修正 Bug #387: 套餐项目回充默认展开并自动加载明细
This commit is contained in:
@@ -4,8 +4,11 @@ import cn.hutool.core.util.ObjectUtil;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
import com.openhis.check.domain.CheckMethod;
|
import com.openhis.check.domain.CheckMethod;
|
||||||
|
import com.openhis.check.domain.CheckPackage;
|
||||||
import com.openhis.check.service.ICheckMethodService;
|
import com.openhis.check.service.ICheckMethodService;
|
||||||
|
import com.openhis.check.service.ICheckPackageService;
|
||||||
import com.openhis.web.check.appservice.ICheckMethodAppService;
|
import com.openhis.web.check.appservice.ICheckMethodAppService;
|
||||||
|
import com.openhis.web.check.dto.CheckMethodDto;
|
||||||
import com.openhis.web.reportmanage.utils.ExcelFillerUtil;
|
import com.openhis.web.reportmanage.utils.ExcelFillerUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -16,6 +19,7 @@ import java.io.IOException;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -24,10 +28,15 @@ public class CheckMethodAppServiceImpl implements ICheckMethodAppService {
|
|||||||
@Resource
|
@Resource
|
||||||
private ICheckMethodService checkMethodService;
|
private ICheckMethodService checkMethodService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ICheckPackageService checkPackageService; // Bug #384修复:注入套餐服务
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public R<?> getCheckMethodList() {
|
public R<?> getCheckMethodList() {
|
||||||
List<CheckMethod> list = checkMethodService.list();
|
List<CheckMethod> list = checkMethodService.list();
|
||||||
return R.ok(list);
|
// Bug #384修复:转换为DTO并关联套餐价格
|
||||||
|
List<CheckMethodDto> dtoList = convertToDtoWithPackagePrice(list);
|
||||||
|
return R.ok(dtoList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -43,7 +52,67 @@ public class CheckMethodAppServiceImpl implements ICheckMethodAppService {
|
|||||||
wrapper.eq(CheckMethod::getPackageName, packageName);
|
wrapper.eq(CheckMethod::getPackageName, packageName);
|
||||||
}
|
}
|
||||||
List<CheckMethod> list = checkMethodService.list(wrapper);
|
List<CheckMethod> list = checkMethodService.list(wrapper);
|
||||||
return R.ok(list);
|
// Bug #384修复:转换为DTO并关联套餐价格
|
||||||
|
List<CheckMethodDto> dtoList = convertToDtoWithPackagePrice(list);
|
||||||
|
return R.ok(dtoList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bug #384修复:转换CheckMethod为DTO,并通过packageName关联查询套餐价格
|
||||||
|
* @param methods 检查方法列表
|
||||||
|
* @return 包含套餐价格的DTO列表
|
||||||
|
*/
|
||||||
|
private List<CheckMethodDto> convertToDtoWithPackagePrice(List<CheckMethod> methods) {
|
||||||
|
if (methods == null || methods.isEmpty()) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有packageName,批量查询套餐
|
||||||
|
List<String> packageNames = methods.stream()
|
||||||
|
.map(CheckMethod::getPackageName)
|
||||||
|
.filter(ObjectUtil::isNotEmpty)
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// Bug #384修复: 批量查询套餐信息,使用final变量
|
||||||
|
final Map<String, CheckPackage> packageMap;
|
||||||
|
if (!packageNames.isEmpty()) {
|
||||||
|
List<CheckPackage> packages = checkPackageService.list(
|
||||||
|
new LambdaQueryWrapper<CheckPackage>()
|
||||||
|
.in(CheckPackage::getPackageName, packageNames)
|
||||||
|
.eq(CheckPackage::getIsDisabled, 0) // 只查未停用的套餐
|
||||||
|
);
|
||||||
|
packageMap = packages.stream()
|
||||||
|
.collect(Collectors.toMap(CheckPackage::getPackageName, p -> p, (p1, p2) -> p1));
|
||||||
|
} else {
|
||||||
|
packageMap = Map.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换为DTO并填充价格
|
||||||
|
return methods.stream().map(m -> {
|
||||||
|
CheckMethodDto dto = new CheckMethodDto();
|
||||||
|
dto.setId(m.getId() != null ? m.getId().longValue() : null);
|
||||||
|
dto.setCheckType(m.getCheckType());
|
||||||
|
dto.setCode(m.getCode());
|
||||||
|
dto.setName(m.getName());
|
||||||
|
dto.setPackageName(m.getPackageName());
|
||||||
|
dto.setExposureNum(m.getExposureNum());
|
||||||
|
dto.setOrderNum(m.getOrderNum());
|
||||||
|
dto.setRemark(m.getRemark());
|
||||||
|
dto.setCreateTime(m.getCreateTime());
|
||||||
|
dto.setUpdateTime(m.getUpdateTime());
|
||||||
|
|
||||||
|
// 通过packageName匹配套餐价格
|
||||||
|
if (ObjectUtil.isNotEmpty(m.getPackageName())) {
|
||||||
|
CheckPackage pkg = packageMap.get(m.getPackageName());
|
||||||
|
if (pkg != null) {
|
||||||
|
dto.setPackagePrice(pkg.getPackagePrice());
|
||||||
|
dto.setServiceFee(pkg.getServiceFee());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
package com.openhis.web.check.dto;
|
package com.openhis.web.check.dto;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查方法DTO - Bug #384修复:增加套餐价格字段
|
||||||
|
* 用于API返回数据传输,不含数据库注解
|
||||||
|
*/
|
||||||
@Data
|
@Data
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
public class CheckMethodDto {
|
public class CheckMethodDto {
|
||||||
@@ -14,7 +17,6 @@ public class CheckMethodDto {
|
|||||||
/**
|
/**
|
||||||
* 检查方法ID
|
* 检查方法ID
|
||||||
*/
|
*/
|
||||||
@TableId(type = IdType.AUTO)
|
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
/* 检查类型 */
|
/* 检查类型 */
|
||||||
@@ -29,6 +31,12 @@ public class CheckMethodDto {
|
|||||||
/* 套餐名称 */
|
/* 套餐名称 */
|
||||||
private String packageName;
|
private String packageName;
|
||||||
|
|
||||||
|
/* 套餐价格 - Bug #384修复:通过packageName匹配CheckPackage获取 */
|
||||||
|
private BigDecimal packagePrice;
|
||||||
|
|
||||||
|
/* 服务费 - Bug #384修复:通过packageName匹配CheckPackage获取 */
|
||||||
|
private BigDecimal serviceFee;
|
||||||
|
|
||||||
/* 曝光次数 */
|
/* 曝光次数 */
|
||||||
private Integer exposureNum;
|
private Integer exposureNum;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.core.common.utils.SecurityUtils;
|
|||||||
import com.openhis.common.enums.DbOpType;
|
import com.openhis.common.enums.DbOpType;
|
||||||
import com.openhis.administration.service.IAccountService;
|
import com.openhis.administration.service.IAccountService;
|
||||||
import com.openhis.administration.domain.Account;
|
import com.openhis.administration.domain.Account;
|
||||||
|
import com.openhis.administration.service.IChargeItemService; // Bug #386修复: 添加 ChargeItemService
|
||||||
import com.openhis.lab.domain.InspectionLabApply;
|
import com.openhis.lab.domain.InspectionLabApply;
|
||||||
import com.openhis.lab.domain.InspectionLabApplyItem;
|
import com.openhis.lab.domain.InspectionLabApplyItem;
|
||||||
import com.openhis.lab.domain.BarCode;
|
import com.openhis.lab.domain.BarCode;
|
||||||
@@ -97,6 +98,10 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ILabActivityDefinitionService labActivityDefinitionService;
|
private ILabActivityDefinitionService labActivityDefinitionService;
|
||||||
|
|
||||||
|
// Bug #386修复: ChargeItemService 用于删除收费项目
|
||||||
|
@Autowired
|
||||||
|
private IChargeItemService chargeItemService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存检验申请单信息
|
* 保存检验申请单信息
|
||||||
* @param doctorStationLabApplyDto
|
* @param doctorStationLabApplyDto
|
||||||
@@ -600,6 +605,12 @@ public class DoctorStationLabApplyServiceImpl implements IDoctorStationInspectio
|
|||||||
if (updateResult) {
|
if (updateResult) {
|
||||||
log.debug("成功将申请单号 [{}] 关联的 {} 条门诊医嘱的删除状态更新为1,更新人:{},更新时间:{}",
|
log.debug("成功将申请单号 [{}] 关联的 {} 条门诊医嘱的删除状态更新为1,更新人:{},更新时间:{}",
|
||||||
applyNo, requestIds.size(), currentUsername, currentTime);
|
applyNo, requestIds.size(), currentUsername, currentTime);
|
||||||
|
|
||||||
|
// Bug #386修复: 同步删除关联的收费项目
|
||||||
|
for (Long requestId : requestIds) {
|
||||||
|
chargeItemService.deleteByServiceTableAndId("wor_service_request", requestId);
|
||||||
|
}
|
||||||
|
log.debug("成功删除申请单号 [{}] 关联的 {} 条收费项目", applyNo, requestIds.size());
|
||||||
} else {
|
} else {
|
||||||
log.warn("更新申请单号 [{}] 关联的门诊医嘱删除状态失败", applyNo);
|
log.warn("更新申请单号 [{}] 关联的门诊医嘱删除状态失败", applyNo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,14 +104,14 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="申请科室" prop="applyDeptCode">
|
<el-form-item label="申请科室" prop="applyDeptCode">
|
||||||
<el-input v-model="form.applyDeptCode" />
|
<el-input v-model="form.applyDeptCode" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row :gutter="12">
|
<el-row :gutter="12">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="申请医生" prop="applyDocCode">
|
<el-form-item label="申请医生" prop="applyDocCode">
|
||||||
<el-input v-model="form.applyDocCode" />
|
<el-input v-model="form.applyDocCode" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
@@ -184,16 +184,10 @@
|
|||||||
<el-input v-model="form.inspectionArea" readonly />
|
<el-input v-model="form.inspectionArea" readonly />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<!-- Bug #384修复: 添加检查方法只读输入框,联动显示选中的检查方法 -->
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-form-item label="检查方法">
|
<el-form-item label="检查方法">
|
||||||
<el-select v-model="form.inspectionMethod" placeholder="请选择" clearable filterable style="width: 100%;">
|
<el-input v-model="form.selectedMethodDisplay" readonly placeholder="请在右侧选择" />
|
||||||
<el-option
|
|
||||||
v-for="method in availableMethods"
|
|
||||||
:key="method.id"
|
|
||||||
:label="method.name"
|
|
||||||
:value="method.name"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -233,16 +227,34 @@
|
|||||||
<el-input v-model="scope.row.applyPart" size="small" />
|
<el-input v-model="scope.row.applyPart" size="small" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column label="检查方法" min-width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<!-- Bug #384修复: 显示检查方法名称,不显示套餐名称 -->
|
||||||
|
<span v-if="scope.row.selectedMethod">
|
||||||
|
{{ scope.row.selectedMethod.name }}
|
||||||
|
</span>
|
||||||
|
<span v-else-if="scope.row.methods && scope.row.methods.length > 0" style="color: #909399;">
|
||||||
|
未选择
|
||||||
|
</span>
|
||||||
|
<span v-else style="color: #c0c4cc;">-</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="单位" prop="unit" width="55" align="center" />
|
<el-table-column label="单位" prop="unit" width="55" align="center" />
|
||||||
<el-table-column label="总量" prop="quantity" width="70" align="center">
|
<el-table-column label="总量" prop="quantity" width="70" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input-number v-model="scope.row.quantity" :min="1" size="small" :controls="false" style="width:100%" />
|
<el-input-number v-model="scope.row.quantity" :min="1" size="small" :controls="false" style="width:100%" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="单价" prop="price" width="75" align="right" />
|
<!-- Bug #384修复: 单价显示套餐价格(如果选中)或部位价格 -->
|
||||||
|
<el-table-column label="单价" width="75" align="right">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.selectedMethod?.packagePrice || scope.row.price }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<!-- Bug #384修复: 金额使用有效价格计算 -->
|
||||||
<el-table-column label="金额" width="80" align="right">
|
<el-table-column label="金额" width="80" align="right">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ ((scope.row.price || 0) * (scope.row.quantity || 1)).toFixed(2) }}
|
{{ ((scope.row.selectedMethod?.packagePrice || scope.row.price || 0) * (scope.row.quantity || 1)).toFixed(2) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="类型" prop="checkType" width="70" align="center" />
|
<el-table-column label="类型" prop="checkType" width="70" align="center" />
|
||||||
@@ -307,22 +319,48 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 右侧:已选择 tags -->
|
<!-- 右侧:已选择 项目卡片(可展开显示检查方法) -->
|
||||||
<div class="selected-panel">
|
<div class="selected-panel">
|
||||||
<div class="panel-label">已选择:</div>
|
<div class="panel-label">已选择:</div>
|
||||||
<div class="selected-tags">
|
<div class="selected-tags">
|
||||||
<div v-if="selectedItems.length === 0" class="empty-selected">–</div>
|
<div v-if="selectedItems.length === 0" class="empty-selected">–</div>
|
||||||
<el-tag
|
<div
|
||||||
v-else
|
v-else
|
||||||
v-for="(item, idx) in selectedItems"
|
v-for="(item, idx) in selectedItems"
|
||||||
:key="idx"
|
:key="idx"
|
||||||
closable
|
class="selected-item-card"
|
||||||
size="small"
|
|
||||||
@close="handleRemoveItem(idx, item)"
|
|
||||||
class="selected-tag"
|
|
||||||
>
|
>
|
||||||
{{ item.name }} ¥{{ item.price }}
|
<!-- Bug #384修复: 项目卡片头部,可展开/收起 -->
|
||||||
</el-tag>
|
<div class="card-header" @click="toggleItemExpand(item)">
|
||||||
|
<span class="card-name">{{ item.name }}</span>
|
||||||
|
<span class="card-price">¥{{ item.price }}</span>
|
||||||
|
<!-- 展开图标 -->
|
||||||
|
<el-icon :class="['expand-icon', { expanded: item.expanded }]">
|
||||||
|
<ArrowDown v-if="!item.expanded" />
|
||||||
|
<ArrowUp v-if="item.expanded" />
|
||||||
|
</el-icon>
|
||||||
|
<!-- 删除按钮 -->
|
||||||
|
<el-button link type="danger" size="small" @click.stop="handleRemoveItem(idx, item)">
|
||||||
|
<el-icon><Close /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<!-- Bug #384修复: 展开后显示检查方法勾选框列表 -->
|
||||||
|
<div v-if="item.expanded && item.methods && item.methods.length > 0" class="method-list">
|
||||||
|
<div
|
||||||
|
v-for="method in item.methods"
|
||||||
|
:key="method.id"
|
||||||
|
class="method-option"
|
||||||
|
>
|
||||||
|
<el-checkbox
|
||||||
|
:model-value="item.selectedMethod?.id === method.id"
|
||||||
|
@change="(val) => selectMethodCheckbox(val, item, method)"
|
||||||
|
>
|
||||||
|
<span class="method-name">{{ method.name }}</span>
|
||||||
|
<span class="method-price">¥{{ method.packagePrice || item.price }}</span>
|
||||||
|
</el-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -334,10 +372,11 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, reactive, computed, watch, onMounted, nextTick } from 'vue';
|
import { ref, reactive, computed, watch, onMounted, nextTick } from 'vue';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import { Printer, Delete } from '@element-plus/icons-vue';
|
import { Printer, Delete, ArrowDown, ArrowUp, Close } from '@element-plus/icons-vue';
|
||||||
import useUserStore from '@/store/modules/user';
|
import useUserStore from '@/store/modules/user';
|
||||||
import request from '@/utils/request';
|
import request from '@/utils/request';
|
||||||
import { listCheckMethod } from '@/api/system/checkType';
|
import { listCheckMethod, searchCheckMethod } from '@/api/system/checkType';
|
||||||
|
import { getEncounterDiagnosis } from '../api.js';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
patientInfo: { type: Object, default: () => ({}) },
|
patientInfo: { type: Object, default: () => ({}) },
|
||||||
@@ -384,7 +423,8 @@ const form = reactive({
|
|||||||
isCharged: 0,
|
isCharged: 0,
|
||||||
isRefunded: 0,
|
isRefunded: 0,
|
||||||
isExecuted: 0,
|
isExecuted: 0,
|
||||||
examTypeCode: '' // 检查类型编码,必填字段,保存时从已选项目自动推导
|
examTypeCode: '', // 检查类型编码,必填字段,保存时从已选项目自动推导
|
||||||
|
selectedMethodDisplay: '' // Bug #384修复: 检查方法显示字段(联动)
|
||||||
});
|
});
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
@@ -590,6 +630,7 @@ async function loadCategoryList() {
|
|||||||
unit: '次',
|
unit: '次',
|
||||||
checkType: p.checkType || '',
|
checkType: p.checkType || '',
|
||||||
nationalCode: p.nationalCode || '',
|
nationalCode: p.nationalCode || '',
|
||||||
|
packageName: p.packageName || '',
|
||||||
checked: false
|
checked: false
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -631,9 +672,11 @@ const filteredCategoryList = computed(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// ====== 合计 ======
|
// ====== 合计 ======
|
||||||
|
// Bug #384修复: 如果选中了检查方法,使用套餐价格;否则使用部位价格
|
||||||
const totalAmountCalc = computed(() => {
|
const totalAmountCalc = computed(() => {
|
||||||
const total = selectedItems.value.reduce((sum, item) => {
|
const total = selectedItems.value.reduce((sum, item) => {
|
||||||
return sum + (item.price * (item.quantity || 1));
|
const effectivePrice = item.selectedMethod?.packagePrice || item.price;
|
||||||
|
return sum + (effectivePrice * (item.quantity || 1));
|
||||||
}, 0);
|
}, 0);
|
||||||
return total.toFixed(2);
|
return total.toFixed(2);
|
||||||
});
|
});
|
||||||
@@ -652,19 +695,49 @@ watch(() => props.patientInfo, (newVal) => {
|
|||||||
}
|
}
|
||||||
}, { immediate: true, deep: true });
|
}, { immediate: true, deep: true });
|
||||||
|
|
||||||
watch(() => props.activeTab, (val) => {
|
watch(() => props.activeTab, async (val) => {
|
||||||
if (val === 'examination') getList();
|
if (val === 'examination') {
|
||||||
|
getList();
|
||||||
|
// 切换到检查页签时,重新获取临床诊断(确保与诊断页签同步)
|
||||||
|
if (props.patientInfo?.encounterId) {
|
||||||
|
await loadClinicalDiag();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function initPatientForm(patient) {
|
function initPatientForm(patient) {
|
||||||
form.patientName = patient.patientName || '';
|
form.patientName = patient.patientName || '';
|
||||||
form.medicalrecordNumber = patient.busNo || patient.visitNo || '';
|
// 就诊卡号应取值于 identifierNo,而非 busNo(busNo 是病历号)
|
||||||
|
form.medicalrecordNumber = patient.identifierNo || patient.visitNo || '';
|
||||||
form.patientId = patient.patientId || '';
|
form.patientId = patient.patientId || '';
|
||||||
form.visitNo = patient.visitNo || '';
|
form.visitNo = patient.visitNo || '';
|
||||||
form.applyDeptCode = userStore.orgName || patient.organizationName || '';
|
form.applyDeptCode = userStore.orgName || patient.organizationName || '';
|
||||||
form.applyDocCode = userStore.nickName || '';
|
form.applyDocCode = userStore.nickName || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载临床诊断:获取患者主诊断并填充到临床诊断字段
|
||||||
|
async function loadClinicalDiag() {
|
||||||
|
if (!props.patientInfo?.encounterId) return;
|
||||||
|
try {
|
||||||
|
const res = await getEncounterDiagnosis(props.patientInfo.encounterId);
|
||||||
|
const diagnoses = res.data || res.rows || res;
|
||||||
|
if (Array.isArray(diagnoses) && diagnoses.length > 0) {
|
||||||
|
// Bug #380, #381 修复: 主诊断字段名为 maindiseFlag (后端 DiagnosisQueryDto 定义)
|
||||||
|
const mainDiag = diagnoses.find(d => d.maindiseFlag === 1 || d.maindiseFlag === '1');
|
||||||
|
// 如果有主诊断使用主诊断,否则使用第一个诊断
|
||||||
|
const targetDiag = mainDiag || diagnoses[0];
|
||||||
|
// 优先使用 diagnosisName,其次是 conditionName 或 name
|
||||||
|
form.clinicalDiag = targetDiag.diagnosisName || targetDiag.conditionName || targetDiag.name || '';
|
||||||
|
} else {
|
||||||
|
// 如果没有诊断,清空临床诊断字段
|
||||||
|
form.clinicalDiag = '';
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('加载临床诊断失败', err);
|
||||||
|
// 获取失败时不阻断用户操作,保持字段为空
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ====== 申请单 CRUD ======
|
// ====== 申请单 CRUD ======
|
||||||
function getList() {
|
function getList() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
@@ -682,24 +755,30 @@ function getList() {
|
|||||||
function handleAdd() {
|
function handleAdd() {
|
||||||
formRef.value?.resetFields();
|
formRef.value?.resetFields();
|
||||||
Object.assign(form, {
|
Object.assign(form, {
|
||||||
applyNo: '', patientId: props.patientInfo?.patientId || '',
|
applyNo: '',
|
||||||
|
patientId: props.patientInfo?.patientId || '',
|
||||||
visitNo: props.patientInfo?.visitNo || '',
|
visitNo: props.patientInfo?.visitNo || '',
|
||||||
|
// 保留患者姓名和就诊卡号,不应重置为空
|
||||||
|
patientName: props.patientInfo?.patientName || '',
|
||||||
|
medicalrecordNumber: props.patientInfo?.identifierNo || '',
|
||||||
applyDeptCode: userStore.orgName || '',
|
applyDeptCode: userStore.orgName || '',
|
||||||
performDeptCode: '',
|
performDeptCode: '',
|
||||||
applyDocCode: userStore.nickName || '',
|
applyDocCode: userStore.nickName || '',
|
||||||
applyTime: new Date().toISOString().split('T')[0] + ' 12:00:00',
|
applyTime: new Date().toISOString().split('T')[0] + ' 12:00:00',
|
||||||
medicalrecordNumber: props.patientInfo?.busNo || '',
|
|
||||||
natureofCost: '自费医疗',
|
natureofCost: '自费医疗',
|
||||||
clinicDesc: '', contraindication: '', medicalHistorySummary: '',
|
clinicDesc: '', contraindication: '', medicalHistorySummary: '',
|
||||||
purposeofInspection: '', inspectionArea: '', inspectionMethod: '',
|
purposeofInspection: '', inspectionArea: '', inspectionMethod: '',
|
||||||
applyRemark: '', clinicalDiag: '', purposeDesc: '',
|
applyRemark: '', clinicalDiag: '', purposeDesc: '',
|
||||||
isUrgent: 0, pregnancyState: 0, allergyDesc: '',
|
isUrgent: 0, pregnancyState: 0, allergyDesc: '',
|
||||||
applyStatus: 0, isCharged: 0, isRefunded: 0, isExecuted: 0,
|
applyStatus: 0, isCharged: 0, isRefunded: 0, isExecuted: 0,
|
||||||
examTypeCode: ''
|
examTypeCode: '',
|
||||||
|
selectedMethodDisplay: '' // Bug #384修复: 重置检查方法显示
|
||||||
});
|
});
|
||||||
selectedItems.value = [];
|
selectedItems.value = [];
|
||||||
resetCategoryChecked();
|
resetCategoryChecked();
|
||||||
activeDetailTab.value = 'applyForm';
|
activeDetailTab.value = 'applyForm';
|
||||||
|
// 自动加载临床诊断
|
||||||
|
loadClinicalDiag();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSave() {
|
function handleSave() {
|
||||||
@@ -713,6 +792,12 @@ function handleSave() {
|
|||||||
const firstCheckType = selectedItems.value[0]?.checkType || 'unknown';
|
const firstCheckType = selectedItems.value[0]?.checkType || 'unknown';
|
||||||
form.examTypeCode = firstCheckType;
|
form.examTypeCode = firstCheckType;
|
||||||
|
|
||||||
|
// 如果有选中的检查方法,更新表单中的检查方法字段(取第一个选中项目的检查方法)
|
||||||
|
const firstItemWithMethod = selectedItems.value.find(item => item.selectedMethod);
|
||||||
|
if (firstItemWithMethod?.selectedMethod) {
|
||||||
|
form.inspectionMethod = firstItemWithMethod.selectedMethod.name;
|
||||||
|
}
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
...form,
|
...form,
|
||||||
encounterId: props.patientInfo?.encounterId || null,
|
encounterId: props.patientInfo?.encounterId || null,
|
||||||
@@ -721,10 +806,16 @@ function handleSave() {
|
|||||||
itemCode: String(item.id),
|
itemCode: String(item.id),
|
||||||
itemName: item.name,
|
itemName: item.name,
|
||||||
bodyPartCode: item.checkType || 'unknown',
|
bodyPartCode: item.checkType || 'unknown',
|
||||||
itemFee: item.price,
|
// Bug #384修复: 如果选中了检查方法且有套餐价格,使用套餐价格;否则使用部位价格
|
||||||
|
itemFee: item.selectedMethod?.packagePrice || item.price,
|
||||||
performDeptCode: form.performDeptCode || '',
|
performDeptCode: form.performDeptCode || '',
|
||||||
itemStatus: 0,
|
itemStatus: 0,
|
||||||
itemSeq: index + 1
|
itemSeq: index + 1,
|
||||||
|
// 检查方法信息
|
||||||
|
checkMethodId: item.selectedMethod?.id || null,
|
||||||
|
checkMethodName: item.selectedMethod?.name || null,
|
||||||
|
checkMethodCode: item.selectedMethod?.code || null,
|
||||||
|
checkMethodPackageName: item.selectedMethod?.packageName || null // Bug #384修复: 保存套餐名称
|
||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
request({
|
request({
|
||||||
@@ -743,22 +834,70 @@ function handleSave() {
|
|||||||
|
|
||||||
function handleRowClick(row) {
|
function handleRowClick(row) {
|
||||||
Object.assign(form, row);
|
Object.assign(form, row);
|
||||||
|
form.selectedMethodDisplay = ''; // Bug #384修复: 先清空,后面根据回充数据更新
|
||||||
selectedItems.value = [];
|
selectedItems.value = [];
|
||||||
activeDetailTab.value = 'applyForm';
|
activeDetailTab.value = 'applyForm';
|
||||||
request({ url: `/exam/apply/${row.applyNo}`, method: 'get' }).then(res => {
|
request({ url: `/exam/apply/${row.applyNo}`, method: 'get' }).then(async res => {
|
||||||
const d = res.data || res;
|
const d = res.data || res;
|
||||||
if (d.data) Object.assign(form, d.data);
|
if (d.data) Object.assign(form, d.data);
|
||||||
if (d.items && Array.isArray(d.items)) {
|
if (d.items && Array.isArray(d.items)) {
|
||||||
selectedItems.value = d.items.map(m => ({
|
try {
|
||||||
|
// 为每个项目加载检查方法
|
||||||
|
const itemsWithMethods = await Promise.all(d.items.map(async m => {
|
||||||
|
const item = {
|
||||||
id: m.itemCode, name: m.itemName,
|
id: m.itemCode, name: m.itemName,
|
||||||
price: m.itemFee || 0, quantity: 1,
|
price: m.itemFee || 0, quantity: 1,
|
||||||
serviceFee: 0, unit: '次',
|
serviceFee: 0, unit: '次',
|
||||||
applyPart: m.itemName,
|
applyPart: m.itemName,
|
||||||
checkType: m.bodyPartCode || '',
|
checkType: m.bodyPartCode || '',
|
||||||
nationalCode: '', checked: true
|
nationalCode: '', checked: true,
|
||||||
}));
|
methods: [],
|
||||||
syncCategoryChecked();
|
selectedMethod: null,
|
||||||
|
expanded: false // Bug #384修复: 添加展开状态
|
||||||
|
};
|
||||||
|
// 加载该项目的检查方法
|
||||||
|
if (m.bodyPartCode) {
|
||||||
|
try {
|
||||||
|
const methodRes = await searchCheckMethod({ checkType: m.bodyPartCode });
|
||||||
|
// Bug #384修复: 正确解析 API 返回结构
|
||||||
|
let methodData = methodRes?.data?.data || methodRes?.data || methodRes?.rows || methodRes;
|
||||||
|
if (!Array.isArray(methodData) && methodRes?.data && Array.isArray(methodRes.data.data)) {
|
||||||
|
methodData = methodRes.data.data;
|
||||||
}
|
}
|
||||||
|
if (Array.isArray(methodData)) {
|
||||||
|
item.methods = methodData.map(md => ({
|
||||||
|
id: md.id,
|
||||||
|
name: md.name,
|
||||||
|
code: md.code,
|
||||||
|
price: m.itemFee || 0, // fallback 到已保存的价格
|
||||||
|
packageName: md.packageName || '',
|
||||||
|
packagePrice: md.packagePrice || null, // Bug #384修复: 套餐价格
|
||||||
|
serviceFee: md.serviceFee || null
|
||||||
|
}));
|
||||||
|
// 如果有已保存的检查方法信息,尝试匹配
|
||||||
|
if (m.checkMethodId) {
|
||||||
|
item.selectedMethod = item.methods.find(md => md.id === m.checkMethodId) || null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('加载检查方法失败', err);
|
||||||
|
// 单个项目加载失败不影响其他项目,继续返回 item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}));
|
||||||
|
selectedItems.value = itemsWithMethods;
|
||||||
|
syncCategoryChecked();
|
||||||
|
// Bug #384修复: 回充后更新检查方法显示
|
||||||
|
updateMethodDisplay();
|
||||||
|
} catch (err) {
|
||||||
|
console.error('加载申请单详情失败', err);
|
||||||
|
ElMessage.error('加载申请单详情失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('获取申请单详情失败', err);
|
||||||
|
ElMessage.error('获取申请单详情失败');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -775,8 +914,37 @@ function handleDelete(row) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ====== 勾选逻辑 ======
|
// ====== 勾选逻辑 ======
|
||||||
function handleItemSelect(checked, item, cat) {
|
async function handleItemSelect(checked, item, cat) {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
|
// Bug #384修复: 检查方法表的 checkType 字段关联的是检查类型的 name(中文名称,如"心电图")
|
||||||
|
const effectiveCheckType = cat?.typeName || cat?.categoryName || '';
|
||||||
|
|
||||||
|
// 查询该检查类型对应的检查方法
|
||||||
|
let methods = [];
|
||||||
|
try {
|
||||||
|
if (effectiveCheckType) {
|
||||||
|
const res = await searchCheckMethod({ checkType: effectiveCheckType });
|
||||||
|
// Bug #384修复: API 返回结构可能是 {data: {data: Array}} 或 {data: Array}
|
||||||
|
let data = res?.data?.data || res?.data || res?.rows || res;
|
||||||
|
if (!Array.isArray(data) && res?.data && Array.isArray(res.data.data)) {
|
||||||
|
data = res.data.data;
|
||||||
|
}
|
||||||
|
if (Array.isArray(data)) {
|
||||||
|
methods = data.map(m => ({
|
||||||
|
id: m.id,
|
||||||
|
name: m.name,
|
||||||
|
code: m.code,
|
||||||
|
price: m.price || item.price, // fallback 到项目价格
|
||||||
|
packageName: m.packageName || '',
|
||||||
|
packagePrice: m.packagePrice || null, // Bug #384修复: 套餐价格
|
||||||
|
serviceFee: m.serviceFee || null // Bug #384修复: 服务费
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('加载检查方法失败', err);
|
||||||
|
}
|
||||||
|
|
||||||
if (selectedItems.value.length > 0) {
|
if (selectedItems.value.length > 0) {
|
||||||
const currentCategory = selectedItems.value[0].checkType;
|
const currentCategory = selectedItems.value[0].checkType;
|
||||||
const newCategory = cat.typeCode || '';
|
const newCategory = cat.typeCode || '';
|
||||||
@@ -793,9 +961,12 @@ function handleItemSelect(checked, item, cat) {
|
|||||||
serviceFee: item.serviceFee || 0,
|
serviceFee: item.serviceFee || 0,
|
||||||
unit: item.unit || '次',
|
unit: item.unit || '次',
|
||||||
applyPart: item.name,
|
applyPart: item.name,
|
||||||
checkType: cat.typeCode || '',
|
checkType: effectiveCheckType, // Bug #384修复: 使用有效的 checkType
|
||||||
nationalCode: item.nationalCode || '',
|
nationalCode: item.nationalCode || '',
|
||||||
checked: true
|
checked: true,
|
||||||
|
methods: methods,
|
||||||
|
selectedMethod: null,
|
||||||
|
expanded: false // Bug #384修复: 新增展开状态,默认不展开
|
||||||
});
|
});
|
||||||
|
|
||||||
// 自动回填执行科室:按检查项目类型 → 检查类型管理里配置的执行科室
|
// 自动回填执行科室:按检查项目类型 → 检查类型管理里配置的执行科室
|
||||||
@@ -804,6 +975,13 @@ function handleItemSelect(checked, item, cat) {
|
|||||||
} else if (!form.performDeptCode && cat?.performDeptName) {
|
} else if (!form.performDeptCode && cat?.performDeptName) {
|
||||||
form.performDeptCode = cat.performDeptName;
|
form.performDeptCode = cat.performDeptName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果有且仅有一个检查方法,自动选中并更新显示
|
||||||
|
if (methods.length === 1) {
|
||||||
|
const lastIdx = selectedItems.value.length - 1;
|
||||||
|
selectedItems.value[lastIdx].selectedMethod = methods[0];
|
||||||
|
updateMethodDisplay(); // Bug #384修复: 联动更新显示
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const idx = selectedItems.value.findIndex(s => s.id === item.id);
|
const idx = selectedItems.value.findIndex(s => s.id === item.id);
|
||||||
if (idx > -1) selectedItems.value.splice(idx, 1);
|
if (idx > -1) selectedItems.value.splice(idx, 1);
|
||||||
@@ -813,11 +991,45 @@ function handleItemSelect(checked, item, cat) {
|
|||||||
form.examTypeCode = '';
|
form.examTypeCode = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 有选项时切换到明细tab
|
// Bug #382 修复:移除自动切换页签逻辑,保持当前页签状态
|
||||||
if (selectedItems.value.length > 0) {
|
}
|
||||||
activeDetailTab.value = 'applyDetail';
|
|
||||||
nextTick(() => detailTableRef.value?.doLayout());
|
// Bug #384修复: 展开/收起项目卡片
|
||||||
|
function toggleItemExpand(item) {
|
||||||
|
item.expanded = !item.expanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bug #384修复: 勾选框选择检查方法(单选逻辑)
|
||||||
|
function selectMethodCheckbox(checked, item, method) {
|
||||||
|
if (checked) {
|
||||||
|
item.selectedMethod = method;
|
||||||
|
} else {
|
||||||
|
item.selectedMethod = null;
|
||||||
}
|
}
|
||||||
|
// 联动更新表单检查方法显示字段
|
||||||
|
updateMethodDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bug #384修复: 更新检查方法显示字段(联动)
|
||||||
|
function updateMethodDisplay() {
|
||||||
|
// 找到第一个有选中检查方法的项目
|
||||||
|
const itemWithMethod = selectedItems.value.find(item => item.selectedMethod);
|
||||||
|
if (itemWithMethod?.selectedMethod) {
|
||||||
|
form.selectedMethodDisplay = itemWithMethod.selectedMethod.name; // 显示检查方法名称,不显示套餐名称
|
||||||
|
} else {
|
||||||
|
form.selectedMethodDisplay = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 选择检查方法
|
||||||
|
function selectMethod(item, method) {
|
||||||
|
if (item.selectedMethod?.id === method.id) {
|
||||||
|
item.selectedMethod = null;
|
||||||
|
} else {
|
||||||
|
item.selectedMethod = method;
|
||||||
|
}
|
||||||
|
// Bug #384修复: 联动更新表单检查方法显示字段
|
||||||
|
updateMethodDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRemoveItem(idx, item) {
|
function handleRemoveItem(idx, item) {
|
||||||
@@ -831,6 +1043,10 @@ function handleRemoveItem(idx, item) {
|
|||||||
if (selectedItems.value.length === 0) {
|
if (selectedItems.value.length === 0) {
|
||||||
form.performDeptCode = '';
|
form.performDeptCode = '';
|
||||||
form.examTypeCode = '';
|
form.examTypeCode = '';
|
||||||
|
form.selectedMethodDisplay = ''; // Bug #384修复: 清空检查方法显示
|
||||||
|
} else {
|
||||||
|
// Bug #384修复: 移除后重新计算检查方法显示
|
||||||
|
updateMethodDisplay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -999,7 +1215,7 @@ defineExpose({ getList });
|
|||||||
|
|
||||||
/* 已选择 tags */
|
/* 已选择 tags */
|
||||||
.selected-panel {
|
.selected-panel {
|
||||||
width: 120px;
|
width: 140px; /* Bug #384修复: 加宽以适应展开内容 */
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -1009,7 +1225,7 @@ defineExpose({ getList });
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 4px;
|
gap: 6px;
|
||||||
}
|
}
|
||||||
.selected-tag {
|
.selected-tag {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
@@ -1022,6 +1238,86 @@ defineExpose({ getList });
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Bug #384修复: 已选择项目卡片(可展开) */
|
||||||
|
.selected-item-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #F5F5F5;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #e4e7ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-item-card .card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-item-card .card-header:hover {
|
||||||
|
background: #E6F7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-name {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #303133;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-price {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #1890FF;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-icon.expanded {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bug #384修复: 检查方法勾选框列表 */
|
||||||
|
.method-list {
|
||||||
|
padding: 6px 10px;
|
||||||
|
background: #fff;
|
||||||
|
border-top: 1px solid #e4e7ed;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.method-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.method-option :deep(.el-checkbox__label) {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.method-option .method-name {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.method-option .method-price {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #e6a23c;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
/* 折叠组件细节 */
|
/* 折叠组件细节 */
|
||||||
:deep(.el-collapse) {
|
:deep(.el-collapse) {
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
@@ -45,35 +45,10 @@
|
|||||||
class="inspection-table"
|
class="inspection-table"
|
||||||
highlight-current-row
|
highlight-current-row
|
||||||
row-key="applicationId"
|
row-key="applicationId"
|
||||||
:expand-row-keys="expandedRowKeys"
|
|
||||||
@expand-change="handleExpandChange"
|
|
||||||
@selection-change="handleSelectionChange"
|
@selection-change="handleSelectionChange"
|
||||||
@current-change="handleRowClick"
|
@current-change="handleRowClick"
|
||||||
@cell-click="handleCellClick"
|
@cell-click="handleCellClick"
|
||||||
>
|
>
|
||||||
<el-table-column type="expand" width="50" align="center" header-align="center">
|
|
||||||
<template #default="scope">
|
|
||||||
<div v-if="scope.row.children && scope.row.children.length > 0" class="expand-content">
|
|
||||||
<el-table :data="scope.row.children" border size="small" style="width: 100%">
|
|
||||||
<el-table-column label="明细项目" prop="itemName" min-width="150" />
|
|
||||||
<el-table-column label="样本类型" prop="sampleType" width="100" />
|
|
||||||
<el-table-column label="单位" prop="unit" width="80" />
|
|
||||||
<el-table-column label="单价" prop="itemPrice" width="80" align="right">
|
|
||||||
<template #default="itemScope">
|
|
||||||
¥{{ formatAmount(itemScope.row.itemPrice) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="数量" prop="itemQty" width="80" align="center" />
|
|
||||||
<el-table-column label="金额" prop="itemAmount" width="80" align="right">
|
|
||||||
<template #default="itemScope">
|
|
||||||
¥{{ formatAmount(itemScope.row.itemAmount) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</div>
|
|
||||||
<div v-else class="expand-empty">无明细项目</div>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column type="selection" width="55" align="center" header-align="center" />
|
<el-table-column type="selection" width="55" align="center" header-align="center" />
|
||||||
<el-table-column label="申请 ID" prop="applicationId" width="80" align="center" header-align="center">
|
<el-table-column label="申请 ID" prop="applicationId" width="80" align="center" header-align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
@@ -83,14 +58,7 @@
|
|||||||
<el-table-column label="申请单号" prop="applyNo" min-width="160" align="center" header-align="center" />
|
<el-table-column label="申请单号" prop="applyNo" min-width="160" align="center" header-align="center" />
|
||||||
<el-table-column label="检验项目" prop="itemName" min-width="170px" align="center" header-align="center">
|
<el-table-column label="检验项目" prop="itemName" min-width="170px" align="center" header-align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span v-if="scope.row.hasChildren" style="color: #409EFF; cursor: pointer" @click.stop="toggleExpand(scope.row)">
|
<span>{{ scope.row.itemName }}</span>
|
||||||
<el-icon style="vertical-align: middle; margin-right: 4px">
|
|
||||||
<Right v-if="!isExpanded(scope.row.applicationId)" />
|
|
||||||
<Bottom v-else />
|
|
||||||
</el-icon>
|
|
||||||
{{ scope.row.itemName }}
|
|
||||||
</span>
|
|
||||||
<span v-else>{{ scope.row.itemName }}</span>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="申请医生" prop="applyDocName" width="120" align="center" header-align="center" />
|
<el-table-column label="申请医生" prop="applyDocName" width="120" align="center" header-align="center" />
|
||||||
@@ -645,7 +613,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import {onMounted, onUnmounted, reactive, ref, watch, computed, getCurrentInstance} from 'vue'
|
import {onMounted, onUnmounted, reactive, ref, watch, computed, getCurrentInstance} from 'vue'
|
||||||
import {ElMessage, ElMessageBox} from 'element-plus'
|
import {ElMessage, ElMessageBox} from 'element-plus'
|
||||||
import { DocumentChecked, Plus, Document, Printer, Delete, Check, Loading, Right, Bottom } from '@element-plus/icons-vue'
|
import { DocumentChecked, Plus, Document, Printer, Delete, Check, Loading } from '@element-plus/icons-vue'
|
||||||
import {
|
import {
|
||||||
deleteInspectionApplication, getApplyList,
|
deleteInspectionApplication, getApplyList,
|
||||||
saveInspectionApplication,
|
saveInspectionApplication,
|
||||||
@@ -792,53 +760,39 @@ const loading = ref(false)
|
|||||||
const saving = ref(false) // 保存状态
|
const saving = ref(false) // 保存状态
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const leftActiveTab = ref('application')
|
const leftActiveTab = ref('application')
|
||||||
const expandedRowKeys = ref([])
|
|
||||||
|
|
||||||
const isExpanded = (applicationId) => {
|
/**
|
||||||
return expandedRowKeys.value.includes(applicationId)
|
* 加载套餐明细(公共函数)
|
||||||
}
|
* @param {string|number} packageId 套餐ID
|
||||||
|
* @returns {Promise<Array>} 明细数组
|
||||||
const toggleExpand = async (row) => {
|
*/
|
||||||
const applicationId = row.applicationId
|
const fetchPackageDetails = async (packageId) => {
|
||||||
const isCurrentlyExpanded = isExpanded(applicationId)
|
if (!packageId) return []
|
||||||
|
|
||||||
if (isCurrentlyExpanded) {
|
|
||||||
// 收起
|
|
||||||
expandedRowKeys.value = expandedRowKeys.value.filter(id => id !== applicationId)
|
|
||||||
} else {
|
|
||||||
// 展开 - 先检查是否需要加载明细数据
|
|
||||||
if (row.hasChildren && (!row.children || row.children.length === 0)) {
|
|
||||||
// 加载套餐明细
|
|
||||||
await loadPackageDetails(row)
|
|
||||||
}
|
|
||||||
expandedRowKeys.value = [...expandedRowKeys.value, applicationId]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleExpandChange = (row, expandedRows) => {
|
|
||||||
expandedRowKeys.value = expandedRows.map(r => r.applicationId)
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadPackageDetails = async (row) => {
|
|
||||||
try {
|
try {
|
||||||
const packageId = row.feePackageId || row.packageId
|
|
||||||
if (!packageId) return
|
|
||||||
|
|
||||||
const res = await getInspectionPackageDetails(packageId)
|
const res = await getInspectionPackageDetails(packageId)
|
||||||
if (res.code === 200 && res.data) {
|
if (res.code === 200 && res.data) {
|
||||||
row.children = res.data.map(detail => ({
|
return res.data.map(detail => {
|
||||||
itemId: detail.detailId || detail.id || detail.itemId,
|
const detailId = detail.detailId || detail.id || detail.itemId
|
||||||
|
const qty = detail.quantity || detail.itemQty || detail.qty || 1
|
||||||
|
const price = detail.unitPrice || detail.itemPrice || detail.price || 0
|
||||||
|
return {
|
||||||
|
detailId: detailId,
|
||||||
|
itemId: detailId, // 兼容表格 row-key
|
||||||
itemName: detail.itemName || detail.name,
|
itemName: detail.itemName || detail.name,
|
||||||
sampleType: detail.sampleType || '',
|
sampleType: detail.sampleType || '',
|
||||||
unit: detail.unit || '',
|
unit: detail.unit || '',
|
||||||
itemPrice: detail.unitPrice || detail.itemPrice || detail.price || 0,
|
quantity: qty,
|
||||||
itemQty: detail.quantity || detail.itemQty || detail.qty || 1,
|
itemQty: qty, // 兼容表格"总量"列
|
||||||
itemAmount: (detail.unitPrice || detail.itemPrice || 0) * (detail.quantity || detail.itemQty || 1)
|
unitPrice: price,
|
||||||
}))
|
itemPrice: price, // 兼容表格"单价"列
|
||||||
|
itemAmount: price * qty
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return []
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载套餐明细失败:', error)
|
console.error('加载套餐明细失败:', error)
|
||||||
row.children = []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -847,35 +801,9 @@ const loadPackageDetailsForTable = async (row, treeNode, resolve) => {
|
|||||||
resolve([])
|
resolve([])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
const packageId = row.feePackageId || row.packageId
|
const packageId = row.feePackageId || row.packageId
|
||||||
if (!packageId) {
|
const children = await fetchPackageDetails(packageId)
|
||||||
resolve([])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await getInspectionPackageDetails(packageId)
|
|
||||||
if (res.code === 200 && res.data) {
|
|
||||||
// 构建明细数据结构
|
|
||||||
// BugFix: 后端返回字段为 unitPrice 和 quantity,需正确映射
|
|
||||||
const children = res.data.map(detail => ({
|
|
||||||
itemId: detail.detailId || detail.id || detail.itemId,
|
|
||||||
itemName: detail.itemName || detail.name,
|
|
||||||
sampleType: detail.sampleType || '',
|
|
||||||
unit: detail.unit || '',
|
|
||||||
itemPrice: detail.unitPrice || detail.itemPrice || detail.price || 0,
|
|
||||||
itemQty: detail.quantity || detail.itemQty || detail.qty || 1,
|
|
||||||
itemAmount: (detail.unitPrice || detail.itemPrice || 0) * (detail.quantity || detail.itemQty || 1)
|
|
||||||
}))
|
|
||||||
resolve(children)
|
resolve(children)
|
||||||
} else {
|
|
||||||
resolve([])
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载套餐明细失败:', error)
|
|
||||||
resolve([])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const togglePackageExpand = async (item) => {
|
const togglePackageExpand = async (item) => {
|
||||||
@@ -885,28 +813,10 @@ const togglePackageExpand = async (item) => {
|
|||||||
|
|
||||||
if (item.expanded && (!item.children || item.children.length === 0)) {
|
if (item.expanded && (!item.children || item.children.length === 0)) {
|
||||||
item.loading = true
|
item.loading = true
|
||||||
try {
|
|
||||||
const packageId = item.feePackageId || item.packageId
|
const packageId = item.feePackageId || item.packageId
|
||||||
if (packageId) {
|
item.children = await fetchPackageDetails(packageId)
|
||||||
const res = await getInspectionPackageDetails(packageId)
|
|
||||||
if (res.code === 200 && res.data) {
|
|
||||||
item.children = res.data.map(detail => ({
|
|
||||||
detailId: detail.detailId || detail.id || detail.itemId,
|
|
||||||
itemName: detail.itemName || detail.name,
|
|
||||||
unit: detail.unit || '',
|
|
||||||
quantity: detail.quantity || detail.itemQty || detail.qty || 1,
|
|
||||||
unitPrice: detail.unitPrice || detail.itemPrice || detail.price || 0,
|
|
||||||
itemPrice: detail.unitPrice || detail.itemPrice || detail.price || 0
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载套餐明细失败:', error)
|
|
||||||
item.children = []
|
|
||||||
} finally {
|
|
||||||
item.loading = false
|
item.loading = false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 申请日期实时更新定时器
|
// 申请日期实时更新定时器
|
||||||
@@ -1822,6 +1732,26 @@ const clearAllSelected = () => {
|
|||||||
selectedInspectionItems.value = []
|
selectedInspectionItems.value = []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bug #387修复: 同步分类勾选状态
|
||||||
|
const syncCategoryChecked = () => {
|
||||||
|
// 重置所有分类项目的勾选状态
|
||||||
|
inspectionCategories.value.forEach(category => {
|
||||||
|
category.items.forEach(item => {
|
||||||
|
item.checked = false
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// 获取已选项目的ID集合
|
||||||
|
const ids = new Set(selectedInspectionItems.value.map(s => s.itemId))
|
||||||
|
// 同步勾选状态
|
||||||
|
for (const cat of inspectionCategories.value) {
|
||||||
|
for (const item of cat.items) {
|
||||||
|
if (ids.has(item.itemId)) {
|
||||||
|
item.checked = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 分页大小改变
|
// 分页大小改变
|
||||||
const handleSizeChange = (size) => {
|
const handleSizeChange = (size) => {
|
||||||
queryParams.pageSize = size
|
queryParams.pageSize = size
|
||||||
@@ -1977,6 +1907,7 @@ const loadApplicationToForm = async (row) => {
|
|||||||
if (detail.labApplyItemList && detail.labApplyItemList.length > 0) {
|
if (detail.labApplyItemList && detail.labApplyItemList.length > 0) {
|
||||||
// Bug #326修复: 直接使用后端返回的数据,不再从本地缓存查找匹配项
|
// Bug #326修复: 直接使用后端返回的数据,不再从本地缓存查找匹配项
|
||||||
// 后端已返回完整关联信息(activityId、feePackageId、inspectionTypeId、sampleType、unit)
|
// 后端已返回完整关联信息(activityId、feePackageId、inspectionTypeId、sampleType、unit)
|
||||||
|
// Bug #387修复: 套餐项目默认展开,并自动加载明细
|
||||||
selectedInspectionItems.value = detail.labApplyItemList.map(item => {
|
selectedInspectionItems.value = detail.labApplyItemList.map(item => {
|
||||||
const itemId = item.activityId || item.itemId || item.id || Math.random().toString(36).substring(2, 11)
|
const itemId = item.activityId || item.itemId || item.id || Math.random().toString(36).substring(2, 11)
|
||||||
const isPackage = item.feePackageId != null || item.itemName?.includes('套餐')
|
const isPackage = item.feePackageId != null || item.itemName?.includes('套餐')
|
||||||
@@ -1995,12 +1926,25 @@ const loadApplicationToForm = async (row) => {
|
|||||||
feePackageId: item.feePackageId || null,
|
feePackageId: item.feePackageId || null,
|
||||||
activityId: item.activityId || itemId,
|
activityId: item.activityId || itemId,
|
||||||
inspectionTypeId: item.inspectionTypeId || null,
|
inspectionTypeId: item.inspectionTypeId || null,
|
||||||
expanded: false,
|
expanded: isPackage, // Bug #387: 套餐默认展开
|
||||||
children: [],
|
children: [],
|
||||||
childrenLoaded: false,
|
childrenLoaded: !isPackage, // Bug #387: 套餐需加载明细
|
||||||
loading: false
|
loading: false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Bug #387修复: 自动加载套餐明细
|
||||||
|
for (const pkgItem of selectedInspectionItems.value) {
|
||||||
|
if (pkgItem.isPackage && pkgItem.feePackageId) {
|
||||||
|
pkgItem.loading = true
|
||||||
|
pkgItem.children = await fetchPackageDetails(pkgItem.feePackageId)
|
||||||
|
pkgItem.childrenLoaded = true
|
||||||
|
pkgItem.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bug #387修复: 同步分类勾选状态
|
||||||
|
syncCategoryChecked()
|
||||||
} else if (detail.inspectionItem || detail.itemName) {
|
} else if (detail.inspectionItem || detail.itemName) {
|
||||||
// 如果只有项目名称,尝试从本地分类中查找匹配项
|
// 如果只有项目名称,尝试从本地分类中查找匹配项
|
||||||
const itemNames = (detail.inspectionItem || detail.itemName).split(/[+,]/)
|
const itemNames = (detail.inspectionItem || detail.itemName).split(/[+,]/)
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ function handleGetPrescription() {
|
|||||||
getPrescriptionList({
|
getPrescriptionList({
|
||||||
encounterIds: encounterIds,
|
encounterIds: encounterIds,
|
||||||
requestStatus: props.requestStatus,
|
requestStatus: props.requestStatus,
|
||||||
|
therapyEnum: type.value === 1 ? undefined : type.value === 2 ? 1 : 2, // 1=全部(不传), 2=长期(1), 3=临时(2)
|
||||||
pageSize: 10000,
|
pageSize: 10000,
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user