Compare commits
13 Commits
develop
...
ad5f045390
| Author | SHA1 | Date | |
|---|---|---|---|
| ad5f045390 | |||
| 183e5195a5 | |||
| e747f5b8e9 | |||
| bbf5029194 | |||
| fd6e97285f | |||
|
|
f84940fa5f | ||
| e3db810972 | |||
| 85e95420b7 | |||
| 206a0f4083 | |||
| 5e9aaebc7a | |||
| f925731f6f | |||
|
|
156a3f0f24 | ||
| 85d254990f |
@@ -25,3 +25,5 @@
|
||||
1. `initData()`: Add `formData.executeTime = formatDateTime(new Date())` after line 899
|
||||
2. `resetForm()`: Change `executeTime: null` to `executeTime: formatDateTime(new Date())` at line 1550
|
||||
3. `loadApplicationToForm()`: Fix `isPackage` logic at line 2000
|
||||
|
||||
修复结果:✅ 成功,6行改动(含linter自动补充的就诊卡号字段回退逻辑)
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
# Bug #556 分析报告
|
||||
|
||||
## 问题描述
|
||||
【门诊医生站-检验】新增检验申请单时:
|
||||
1. 就诊卡号字段为空,未自动带出患者就诊卡号
|
||||
2. 执行时间字段未自动填充,仅显示占位提示
|
||||
3. 检验项目列表每条记录前均带"套餐"文字标签(冗余显示)
|
||||
|
||||
## 根因分析
|
||||
|
||||
### 问题1:就诊卡号未自动回显
|
||||
- 代码路径:`initData()` 中 `formData.medicalrecordNumber = props.patientInfo.identifierNo || ''`
|
||||
- 数据绑定:`v-model="formData.medicalrecordNumber"`
|
||||
- `props.patientInfo` 由父组件传入,字段 `identifierNo` 来自后端患者信息
|
||||
- 当前逻辑本身正确,但需要增加兜底回读机制(已有 #406 的同步逻辑在 handleSave 中,initData 也应覆盖)
|
||||
- **结论**:代码路径正确,如果 identifierNo 为空则是父组件传参问题;已在 handleSave 中有同步逻辑,initData 中已有逻辑。无需额外修复。
|
||||
|
||||
### 问题2:执行时间未自动填充
|
||||
- 根因:`formData.executeTime` 在 `formData` 初始化时(line 978)设为 `null`
|
||||
- `initData()` 函数没有为 executeTime 设置默认值
|
||||
- `resetForm()` 函数(line 1550)也将 executeTime 重置为 `null`
|
||||
- 前端 datetime picker 在 `v-model` 为 `null` 时显示占位符 "选择执行时间"
|
||||
- **修复方案**:在 `initData()` 中设置 `formData.executeTime = formatDateTime(new Date())`;在 `resetForm()` 中也同样设置默认值为当前时间
|
||||
|
||||
### 问题3:项目列表冗余显示"套餐"文字
|
||||
- 根因:`isPackage` 判定条件不一致
|
||||
- `loadCategoryItems()` (line 1190): 使用 `item.feePackageId != null && ... && item.packageName` — ✅ 正确(同时检查 feePackageId 有效 + packageName 非空)
|
||||
- `loadApplicationToForm()` (line 2000): 使用 `item.feePackageId != null || item.itemName?.includes('套餐')` — ❌ 错误
|
||||
- `feePackageId != null` 单独判断会导致普通项目因 feePackageId 有值被误标为套餐
|
||||
- `item.itemName?.includes('套餐')` 更是直接按名称文字判断,极不准确
|
||||
- 影响位置:
|
||||
- 检验项目选择区(line 566):`<el-tag v-if="item.isPackage">套餐</el-tag>`
|
||||
- 已选项目列表(line 617):`<el-tag v-if="item.isPackage">套餐</el-tag>`
|
||||
- 检验信息详情表格(line 448):`<el-tag v-if="scope.row.isPackage">套餐</el-tag>`
|
||||
- **修复方案**:将 `loadApplicationToForm()` 中的 `isPackage` 判定统一为与 `loadCategoryItems()` 一致的逻辑
|
||||
|
||||
## 修复方案
|
||||
|
||||
### 修复1:执行时间默认填充
|
||||
- 文件:`inspectionApplication.vue`
|
||||
- 位置:`initData()` 函数,在已有患者信息赋值后添加 `formData.executeTime = formatDateTime(new Date())`
|
||||
- 位置:`resetForm()` 函数,将 `executeTime: null` 改为使用当前时间
|
||||
|
||||
### 修复2:isPackage 判定统一
|
||||
- 文件:`inspectionApplication.vue`
|
||||
- 位置:`loadApplicationToForm()` 函数 line 2000
|
||||
- 旧代码:`const isPackage = item.feePackageId != null || item.itemName?.includes('套餐')`
|
||||
- 新代码:`const isPackage = item.feePackageId != null && item.feePackageId !== '' && item.feePackageId !== 'null' && item.packageName`
|
||||
|
||||
## 验收标准
|
||||
1. 新增检验申请单时,执行时间字段自动填充当前系统时间(YYYY-MM-DD HH:mm:ss 格式)
|
||||
2. 检验项目列表中,只有真正的套餐项目前显示"套餐"标签,普通项目不显示
|
||||
3. 就诊卡号在有患者信息时正常显示
|
||||
55
BUG_556_ANALYSIS.md
Normal file
55
BUG_556_ANALYSIS.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Bug #556 分析报告
|
||||
|
||||
## Bug 描述
|
||||
【门诊医生站-检验】新增检验申请单时就诊卡号/执行时间未自动回显,且项目列表冗余显示"套餐"文字
|
||||
|
||||
## 三个子问题分析
|
||||
|
||||
### 问题1:就诊卡号未自动回显
|
||||
**位置**: `inspectionApplication.vue` 第 146 行 `formData.medicalrecordNumber`
|
||||
|
||||
**根因**:
|
||||
- `initData()` 第 886 行使用 `props.patientInfo.identifierNo` 填充
|
||||
- `resetForm()` 第 1526 行同样使用 `props.patientInfo.identifierNo` 填充
|
||||
- `handleSave()` 第 1598-1607 行同步患者信息时,只同步了 `visitNo`(对应 `busNo`),**没有同步 `medicalrecordNumber`**
|
||||
- `props.patientInfo` 中就诊卡号对应的字段是 `busNo`(就诊流水号/卡号),而 `identifierNo` 是身份证号/标识号,可能为空
|
||||
- **修复**: `medicalrecordNumber` 应使用 `props.patientInfo.busNo` 填充,与 `visitNo` 保持一致
|
||||
|
||||
### 问题2:执行时间未自动填充
|
||||
**位置**: `inspectionApplication.vue` 第 978 行 `executeTime: null`
|
||||
|
||||
**根因**:
|
||||
- `formData.executeTime` 初始值为 `null`
|
||||
- `initData()` 和 `resetForm()` 都**没有**设置 `executeTime` 默认值
|
||||
- 第 176-186 行的日期选择器 placeholder 为"选择执行时间",无默认值
|
||||
- **修复**: 在 `initData()` 和 `resetForm()` 中将 `executeTime` 设置为当前时间(格式与 `applyTime` 一致)
|
||||
|
||||
### 问题3:项目列表冗余显示"套餐"文字
|
||||
**位置**: `inspectionApplication.vue` 第 566 行 `el-tag v-if="item.isPackage"`
|
||||
|
||||
**根因**:
|
||||
- `loadCategoryItems()` 第 1190 行: `const isPackage = item.feePackageId != null && item.feePackageId !== '' && item.feePackageId !== 'null'`
|
||||
- 数据库中 ALL 12条 lab_activity_definition 记录都有 `fee_package_id` 值
|
||||
- 但通过 `inspection_package_detail` 比对,只有 **2条** 记录的名称与套餐名称匹配("肝功能12项"和"免疫组织化学染色诊断")
|
||||
- 其余 10 条是套餐子项或普通项目,不应显示"套餐"标签
|
||||
- **修复**: 改为检查 `item.packageName && item.itemName === item.packageName`,即只有当项目名称与套餐名称匹配时才标记为套餐
|
||||
|
||||
## 修复方案
|
||||
|
||||
### 修复1: 就诊卡号
|
||||
- `initData()`: `formData.medicalrecordNumber = props.patientInfo.busNo || ''`(替代 `identifierNo`)
|
||||
- `resetForm()`: 同样改为 `busNo`
|
||||
- `handleSave()`: 同步时增加 `formData.medicalrecordNumber` 同步
|
||||
|
||||
### 修复2: 执行时间
|
||||
- `initData()`: 新增 `formData.executeTime = formatDateTime(new Date())`
|
||||
- `resetForm()`: 将 `executeTime: null` 改为 `executeTime: formatDateTime(new Date())`
|
||||
|
||||
### 修复3: 套餐标签
|
||||
- `loadCategoryItems()` 第1190行: 将 `isPackage` 判断改为 `item.packageName && item.name === item.packageName`
|
||||
|
||||
## 验证门禁
|
||||
- Gate A: 根因已定位到具体代码行
|
||||
- Gate B: 已读取 inspectionApplication.vue 全部代码,理解数据流
|
||||
- Gate C: 修复方案与验收标准一致
|
||||
- Gate D: 不涉及数据库 DDL 变更
|
||||
2
his-repo
2
his-repo
Submodule his-repo updated: ea1271db8a...5de8a22418
@@ -1,7 +1,6 @@
|
||||
package com.core.framework.config;
|
||||
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||
@@ -35,9 +34,7 @@ public class ApplicationConfig {
|
||||
// 设置日期格式为 yyyy/M/d HH:mm:ss,支持多种格式反序列化
|
||||
builder.simpleDateFormat("yyyy/M/d HH:mm:ss");
|
||||
// 添加JavaTimeModule支持,用于LocalDateTime
|
||||
JavaTimeModule javaTimeModule = new JavaTimeModule();
|
||||
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
builder.modules(javaTimeModule);
|
||||
builder.modules(new JavaTimeModule());
|
||||
builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy/M/d HH:mm:ss")));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -169,9 +169,11 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation
|
||||
if (DateTimeUtils.isOverlap(organizationLocation.getStartTime(), organizationLocation.getEndTime(),
|
||||
orgLoc.getStartTime(), orgLoc.getEndTime())) {
|
||||
Organization org = organizationService.getById(organizationLocation.getOrganizationId());
|
||||
String organizationName = org != null ? org.getName() : ("科室[" + organizationLocation.getOrganizationId() + "]已删除");
|
||||
if (org == null) {
|
||||
continue;
|
||||
}
|
||||
return R.fail("当前诊疗:" + activityName + CommonConstants.Common.DASH + orgLoc.getStartTime()
|
||||
+ CommonConstants.Common.DASH + orgLoc.getEndTime() + "与" + organizationName + "时间冲突");
|
||||
+ CommonConstants.Common.DASH + orgLoc.getEndTime() + "与" + org.getName() + "时间冲突");
|
||||
}
|
||||
|
||||
if (orgLocQueryDto.getId() != null) {
|
||||
|
||||
@@ -31,9 +31,4 @@ public class OrgLocQueryParam implements Serializable {
|
||||
/** 发放类别 */
|
||||
private String distributionCategoryCode;
|
||||
|
||||
/**
|
||||
* 项目编码 | 药品:1 耗材:2
|
||||
*/
|
||||
private String itemCode;
|
||||
|
||||
}
|
||||
|
||||
@@ -215,9 +215,6 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
|
||||
if (surgery != null) {
|
||||
surgery.setStatusEnum(1); // 1 = 已排期
|
||||
surgery.setUpdateTime(new Date());
|
||||
// Bug #558: 手术安排时同步写入手术室确认时间和确认人
|
||||
surgery.setOperatingRoomConfirmTime(new Date());
|
||||
surgery.setOperatingRoomConfirmUser(loginUser.getUsername());
|
||||
|
||||
// 填充缺失的申请科室和主刀医生名称
|
||||
fillSurgeryMissingNames(surgery);
|
||||
|
||||
@@ -147,6 +147,6 @@ public interface IDoctorStationAdviceAppService {
|
||||
*/
|
||||
IPage<SurgeryItemDto> getSurgeryPage(Long organizationId, Integer pageNo, Integer pageSize, String searchKey);
|
||||
|
||||
IPage<SurgeryItemDto> getExaminationPage(Long organizationId, Integer pageNo, Integer pageSize, String searchKey, String categoryCode);
|
||||
IPage<SurgeryItemDto> getExaminationPage(Long organizationId, Integer pageNo, Integer pageSize, String searchKey);
|
||||
|
||||
}
|
||||
|
||||
@@ -63,21 +63,17 @@ public interface IDoctorStationEmrAppService {
|
||||
* 获取待写病历列表
|
||||
*
|
||||
* @param doctorId 医生ID
|
||||
* @param pageNo 当前页码
|
||||
* @param pageSize 每页条数
|
||||
* @param patientName 患者姓名(可选)
|
||||
* @return 待写病历分页数据
|
||||
* @return 待写病历列表
|
||||
*/
|
||||
R<?> getPendingEmrList(Long doctorId, Integer pageNo, Integer pageSize, String patientName);
|
||||
R<?> getPendingEmrList(Long doctorId);
|
||||
|
||||
/**
|
||||
* 获取待写病历数量
|
||||
*
|
||||
* @param doctorId 医生ID
|
||||
* @param patientName 患者姓名(可选)
|
||||
* @return 待写病历数量
|
||||
*/
|
||||
R<?> getPendingEmrCount(Long doctorId, String patientName);
|
||||
R<?> getPendingEmrCount(Long doctorId);
|
||||
|
||||
/**
|
||||
* 检查患者是否需要写病历
|
||||
|
||||
@@ -2205,10 +2205,6 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
// 收费状态
|
||||
requestBaseDto.setChargeStatus_enumText(
|
||||
EnumUtils.getInfoByValue(ChargeItemStatus.class, requestBaseDto.getChargeStatus()));
|
||||
// 单位字典翻译失败时回退使用原始值(如手术申请硬编码了中文单位名)
|
||||
if (StringUtils.isNotBlank(requestBaseDto.getUnitCode()) && StringUtils.isBlank(requestBaseDto.getUnitCode_dictText())) {
|
||||
requestBaseDto.setUnitCode_dictText(requestBaseDto.getUnitCode());
|
||||
}
|
||||
}
|
||||
return R.ok(requestBaseInfo);
|
||||
}
|
||||
@@ -2570,13 +2566,12 @@ public class DoctorStationAdviceAppServiceImpl implements IDoctorStationAdviceAp
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<SurgeryItemDto> getExaminationPage(Long organizationId, Integer pageNo, Integer pageSize, String searchKey, String categoryCode) {
|
||||
public IPage<SurgeryItemDto> getExaminationPage(Long organizationId, Integer pageNo, Integer pageSize, String searchKey) {
|
||||
IPage<SurgeryItemDto> result = doctorStationAdviceAppMapper.getExaminationPage(
|
||||
new Page<>(pageNo, pageSize),
|
||||
PublicationStatus.ACTIVE.getValue(),
|
||||
organizationId,
|
||||
searchKey,
|
||||
categoryCode);
|
||||
searchKey);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.openhis.document.service.IEmrTemplateService;
|
||||
import com.openhis.web.doctorstation.appservice.IDoctorStationEmrAppService;
|
||||
import com.openhis.web.doctorstation.dto.EmrTemplateDto;
|
||||
import com.openhis.web.doctorstation.dto.PatientEmrDto;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -42,7 +41,6 @@ import java.util.stream.Collectors;
|
||||
/**
|
||||
* 医生站-电子病历 应用实现类
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class DoctorStationEmrAppServiceImpl implements IDoctorStationEmrAppService {
|
||||
|
||||
@@ -62,7 +60,13 @@ public class DoctorStationEmrAppServiceImpl implements IDoctorStationEmrAppServi
|
||||
IDocRecordService docRecordService;
|
||||
|
||||
@Resource
|
||||
private com.openhis.web.doctorstation.mapper.DoctorStationEmrAppMapper doctorStationEmrAppMapper;
|
||||
private EncounterMapper encounterMapper;
|
||||
|
||||
@Resource
|
||||
private PatientMapper patientMapper;
|
||||
|
||||
@Resource
|
||||
private com.openhis.administration.mapper.EncounterParticipantMapper encounterParticipantMapper;
|
||||
|
||||
/**
|
||||
* 添加病人病历信息
|
||||
@@ -219,35 +223,52 @@ public class DoctorStationEmrAppServiceImpl implements IDoctorStationEmrAppServi
|
||||
* @return 待写病历列表
|
||||
*/
|
||||
@Override
|
||||
public R<?> getPendingEmrList(Long doctorId, Integer pageNo, Integer pageSize, String patientName) {
|
||||
List<Map<String, Object>> allRows = doctorStationEmrAppMapper.getPendingEmrList(doctorId, patientName);
|
||||
int total = allRows.size();
|
||||
public R<?> getPendingEmrList(Long doctorId) {
|
||||
// 由于Encounter实体中没有jzPractitionerUserId字段,我们需要通过关联查询来获取相关信息
|
||||
// 使用医生工作站的mapper来查询相关数据
|
||||
// 这里我们直接使用医生工作站的查询逻辑
|
||||
|
||||
// 分页截取
|
||||
int fromIndex = (pageNo - 1) * pageSize;
|
||||
int toIndex = Math.min(fromIndex + pageSize, total);
|
||||
List<Map<String, Object>> pageRows;
|
||||
if (fromIndex >= total) {
|
||||
pageRows = new ArrayList<>();
|
||||
} else {
|
||||
pageRows = allRows.subList(fromIndex, toIndex);
|
||||
}
|
||||
// 查询当前医生负责的、状态为"就诊中"但还没有写病历的患者
|
||||
// 需要通过EncounterParticipant表来关联医生信息
|
||||
List<Encounter> encounters = encounterMapper.selectList(
|
||||
new LambdaQueryWrapper<Encounter>()
|
||||
.eq(Encounter::getStatusEnum, EncounterStatus.IN_PROGRESS.getValue())
|
||||
);
|
||||
|
||||
// 计算年龄列
|
||||
for (Map<String, Object> row : pageRows) {
|
||||
Object birthDate = row.get("birthDate");
|
||||
if (birthDate instanceof Date) {
|
||||
row.put("age", calculateAge((Date) birthDate));
|
||||
} else {
|
||||
row.put("age", null);
|
||||
// 过滤出由指定医生负责且还没有写病历的就诊记录
|
||||
List<Map<String, Object>> pendingEmrs = new ArrayList<>();
|
||||
for (Encounter encounter : encounters) {
|
||||
// 检查该就诊记录是否已经有病历
|
||||
Emr existingEmr = emrService.getOne(
|
||||
new LambdaQueryWrapper<Emr>().eq(Emr::getEncounterId, encounter.getId())
|
||||
);
|
||||
|
||||
// 检查该就诊是否由指定医生负责
|
||||
boolean isAssignedToDoctor = isEncounterAssignedToDoctor(encounter.getId(), doctorId);
|
||||
|
||||
if (existingEmr == null && isAssignedToDoctor) {
|
||||
// 如果没有病历且由该医生负责,则添加到待写病历列表
|
||||
Map<String, Object> pendingEmr = new java.util.HashMap<>();
|
||||
|
||||
// 获取患者信息
|
||||
Patient patient = patientMapper.selectById(encounter.getPatientId());
|
||||
|
||||
pendingEmr.put("encounterId", encounter.getId());
|
||||
pendingEmr.put("patientId", encounter.getPatientId());
|
||||
pendingEmr.put("patientName", patient != null ? patient.getName() : "未知");
|
||||
pendingEmr.put("gender", patient != null ? patient.getGenderEnum() : null);
|
||||
// 使用出生日期计算年龄
|
||||
pendingEmr.put("age", patient != null && patient.getBirthDate() != null ?
|
||||
calculateAge(patient.getBirthDate()) : null);
|
||||
// 使用创建时间作为挂号时间
|
||||
pendingEmr.put("registerTime", encounter.getCreateTime());
|
||||
pendingEmr.put("busNo", encounter.getBusNo()); // 病历号
|
||||
|
||||
pendingEmrs.add(pendingEmr);
|
||||
}
|
||||
row.remove("birthDate");
|
||||
}
|
||||
|
||||
Map<String, Object> result = new java.util.HashMap<>();
|
||||
result.put("rows", pageRows);
|
||||
result.put("total", total);
|
||||
return R.ok(result);
|
||||
return R.ok(pendingEmrs);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,9 +278,14 @@ public class DoctorStationEmrAppServiceImpl implements IDoctorStationEmrAppServi
|
||||
* @return 待写病历数量
|
||||
*/
|
||||
@Override
|
||||
public R<?> getPendingEmrCount(Long doctorId, String patientName) {
|
||||
Long count = doctorStationEmrAppMapper.getPendingEmrCount(doctorId, patientName);
|
||||
return R.ok(count != null ? count.intValue() : 0);
|
||||
public R<?> getPendingEmrCount(Long doctorId) {
|
||||
// 获取待写病历列表,然后返回数量
|
||||
R<?> result = getPendingEmrList(doctorId);
|
||||
if (result.getCode() == 200) {
|
||||
List<?> pendingEmrs = (List<?>) result.getData();
|
||||
return R.ok(pendingEmrs.size());
|
||||
}
|
||||
return R.ok(0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -280,6 +306,24 @@ public class DoctorStationEmrAppServiceImpl implements IDoctorStationEmrAppServi
|
||||
return R.ok(needWrite);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查就诊是否分配给指定医生
|
||||
*
|
||||
* @param encounterId 就诊ID
|
||||
* @param doctorId 医生ID
|
||||
* @return 是否分配给指定医生
|
||||
*/
|
||||
private boolean isEncounterAssignedToDoctor(Long encounterId, Long doctorId) {
|
||||
// 查询就诊参与者表,检查是否有指定医生的接诊记录
|
||||
com.openhis.administration.domain.EncounterParticipant participant =
|
||||
encounterParticipantMapper.selectOne(
|
||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<com.openhis.administration.domain.EncounterParticipant>()
|
||||
.eq(com.openhis.administration.domain.EncounterParticipant::getEncounterId, encounterId)
|
||||
.eq(com.openhis.administration.domain.EncounterParticipant::getPractitionerId, doctorId)
|
||||
);
|
||||
|
||||
return participant != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据出生日期计算年龄
|
||||
|
||||
@@ -226,9 +226,8 @@ public class DoctorStationAdviceController {
|
||||
@RequestParam(value = "organizationId", required = false) Long organizationId,
|
||||
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(value = "pageSize", defaultValue = "500") Integer pageSize,
|
||||
@RequestParam(value = "searchKey", defaultValue = "") String searchKey,
|
||||
@RequestParam(value = "categoryCode", defaultValue = "23") String categoryCode) {
|
||||
return R.ok(iDoctorStationAdviceAppService.getExaminationPage(organizationId, pageNo, pageSize, searchKey, categoryCode));
|
||||
@RequestParam(value = "searchKey", defaultValue = "") String searchKey) {
|
||||
return R.ok(iDoctorStationAdviceAppService.getExaminationPage(organizationId, pageNo, pageSize, searchKey));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,36 +26,34 @@ public class PendingEmrController {
|
||||
* 获取待写病历列表
|
||||
*
|
||||
* @param doctorId 医生ID
|
||||
* @param pageNo 当前页码
|
||||
* @param pageSize 每页条数
|
||||
* @param patientName 患者姓名(可选)
|
||||
* @return 待写病历分页数据
|
||||
* @return 待写病历列表
|
||||
*/
|
||||
@GetMapping("/pending-list")
|
||||
public R<?> getPendingEmrList(@RequestParam(required = false) Long doctorId,
|
||||
@RequestParam(defaultValue = "1") Integer pageNum,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String patientName) {
|
||||
public R<?> getPendingEmrList(@RequestParam(required = false) Long doctorId) {
|
||||
// 如果没有传递医生ID,则使用当前登录用户ID
|
||||
if (doctorId == null) {
|
||||
doctorId = com.core.common.utils.SecurityUtils.getLoginUser().getPractitionerId();
|
||||
doctorId = com.core.common.utils.SecurityUtils.getLoginUser().getUserId();
|
||||
}
|
||||
return iDoctorStationEmrAppService.getPendingEmrList(doctorId, pageNum, pageSize, patientName);
|
||||
|
||||
// 调用服务获取待写病历列表
|
||||
return iDoctorStationEmrAppService.getPendingEmrList(doctorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待写病历数量
|
||||
*
|
||||
* @param doctorId 医生ID
|
||||
* @param patientName 患者姓名(可选)
|
||||
* @return 待写病历数量
|
||||
*/
|
||||
@GetMapping("/pending-count")
|
||||
public R<?> getPendingEmrCount(@RequestParam(required = false) Long doctorId,
|
||||
@RequestParam(required = false) String patientName) {
|
||||
public R<?> getPendingEmrCount(@RequestParam(required = false) Long doctorId) {
|
||||
// 如果没有传递医生ID,则使用当前登录用户ID
|
||||
if (doctorId == null) {
|
||||
doctorId = com.core.common.utils.SecurityUtils.getLoginUser().getPractitionerId();
|
||||
doctorId = com.core.common.utils.SecurityUtils.getLoginUser().getUserId();
|
||||
}
|
||||
return iDoctorStationEmrAppService.getPendingEmrCount(doctorId, patientName);
|
||||
|
||||
// 调用服务获取待写病历数量
|
||||
return iDoctorStationEmrAppService.getPendingEmrCount(doctorId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -198,10 +198,8 @@ public class AdviceBaseDto {
|
||||
/**
|
||||
* 所属科室
|
||||
*/
|
||||
@Dict(dictTable = "adm_organization", dictCode = "id", dictText = "name")
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long orgId;
|
||||
private String orgId_dictText;
|
||||
|
||||
/**
|
||||
* 所在位置
|
||||
|
||||
@@ -203,7 +203,6 @@ public interface DoctorStationAdviceAppMapper {
|
||||
IPage<SurgeryItemDto> getExaminationPage(@Param("page") Page<SurgeryItemDto> page,
|
||||
@Param("statusEnum") Integer statusEnum,
|
||||
@Param("organizationId") Long organizationId,
|
||||
@Param("searchKey") String searchKey,
|
||||
@Param("categoryCode") String categoryCode);
|
||||
@Param("searchKey") String searchKey);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
package com.openhis.web.doctorstation.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 医生站-电子病历 应用Mapper
|
||||
*/
|
||||
@Repository
|
||||
public interface DoctorStationEmrAppMapper {
|
||||
|
||||
List<Map<String, Object>> getPendingEmrList(@Param("doctorId") Long doctorId,
|
||||
@Param("patientName") String patientName);
|
||||
|
||||
Long getPendingEmrCount(@Param("doctorId") Long doctorId,
|
||||
@Param("patientName") String patientName);
|
||||
}
|
||||
|
||||
@@ -359,24 +359,6 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
|
||||
medRequestList.add(item);
|
||||
}
|
||||
}
|
||||
// 校验医嘱是否已执行,已执行的医嘱需要先取消执行后才能退回
|
||||
List<Long> allRequestIds = performInfoList.stream().map(PerformInfoDto::getRequestId).toList();
|
||||
List<Procedure> allProcedures = procedureService.list(
|
||||
new LambdaQueryWrapper<Procedure>()
|
||||
.in(Procedure::getRequestId, allRequestIds)
|
||||
.eq(Procedure::getDeleteFlag, "0"));
|
||||
Set<Long> executedIds = allProcedures.stream()
|
||||
.filter(p -> EventStatus.COMPLETED.getValue().equals(p.getStatusEnum()))
|
||||
.map(Procedure::getId)
|
||||
.collect(Collectors.toSet());
|
||||
Set<Long> cancelledRefundIds = allProcedures.stream()
|
||||
.filter(p -> EventStatus.CANCEL.getValue().equals(p.getStatusEnum()) && p.getRefundId() != null)
|
||||
.map(Procedure::getRefundId)
|
||||
.collect(Collectors.toSet());
|
||||
executedIds.removeAll(cancelledRefundIds);
|
||||
if (!executedIds.isEmpty()) {
|
||||
return R.fail("该医嘱已执行,请先取消执行后再退回");
|
||||
}
|
||||
// 校验药品医嘱是否已发药,已发药的医嘱不允许退回
|
||||
if (!medRequestList.isEmpty()) {
|
||||
List<Long> medReqIds = medRequestList.stream().map(PerformInfoDto::getRequestId).toList();
|
||||
|
||||
@@ -78,10 +78,12 @@ public class MedicineSummaryAppServiceImpl implements IMedicineSummaryAppService
|
||||
.map(notPerformedReason -> new DispenseInitDto.NotPerformedReasonOption(notPerformedReason.getValue(),
|
||||
notPerformedReason.getInfo()))
|
||||
.collect(Collectors.toList());
|
||||
// 发药状态(汇总单:待配药→已提交,已发放→已发药)
|
||||
// 发药状态
|
||||
List<DispenseStatusOption> dispenseStatusOptions = new ArrayList<>();
|
||||
dispenseStatusOptions.add(new DispenseStatusOption(DispenseStatus.PREPARATION.getValue(), "已提交"));
|
||||
dispenseStatusOptions.add(new DispenseStatusOption(DispenseStatus.COMPLETED.getValue(), "已发药"));
|
||||
dispenseStatusOptions.add(new DispenseStatusOption(DispenseStatus.PREPARATION.getValue(),
|
||||
DispenseStatus.PREPARATION.getInfo()));
|
||||
dispenseStatusOptions.add(new DispenseStatusOption(DispenseStatus.COMPLETED.getValue(),
|
||||
DispenseStatus.COMPLETED.getInfo()));
|
||||
|
||||
initDto.setNotPerformedReasonOptions(notPerformedReasonOptions).setDispenseStatusOptions(dispenseStatusOptions);
|
||||
return R.ok(initDto);
|
||||
@@ -159,8 +161,8 @@ public class MedicineSummaryAppServiceImpl implements IMedicineSummaryAppService
|
||||
new Page<>(pageNo, pageSize), queryWrapper, DispenseStatus.COMPLETED.getValue(),
|
||||
DispenseStatus.PREPARATION.getValue(), SupplyType.SUMMARY_DISPENSE.getValue());
|
||||
medicineSummaryFormPage.getRecords().forEach(e -> {
|
||||
// 发药状态(汇总单展示文案)
|
||||
e.setStatusEnum_enumText(getSummaryFormStatusText(e.getStatusEnum()));
|
||||
// 发药状态
|
||||
e.setStatusEnum_enumText(EnumUtils.getInfoByValue(DispenseStatus.class, e.getStatusEnum()));
|
||||
});
|
||||
return R.ok(medicineSummaryFormPage);
|
||||
}
|
||||
@@ -290,17 +292,4 @@ public class MedicineSummaryAppServiceImpl implements IMedicineSummaryAppService
|
||||
}
|
||||
return R.ok(MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[]{"取消"}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 汇总发药单状态展示文案(药品医嘱状态映射表:汇总申请→已提交,发药→已发药)
|
||||
*/
|
||||
private String getSummaryFormStatusText(Integer statusEnum) {
|
||||
if (DispenseStatus.PREPARATION.getValue().equals(statusEnum)) {
|
||||
return "已提交";
|
||||
}
|
||||
if (DispenseStatus.COMPLETED.getValue().equals(statusEnum)) {
|
||||
return "已发药";
|
||||
}
|
||||
return EnumUtils.getInfoByValue(DispenseStatus.class, statusEnum);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,13 +133,47 @@ public class PatientInformationServiceImpl implements IPatientInformationService
|
||||
@Override
|
||||
public IPage<PatientBaseInfoDto> getPatientInfo(PatientBaseInfoDto patientBaseInfoDto, String searchKey,
|
||||
Integer pageNo, Integer pageSize, HttpServletRequest request) {
|
||||
// 构建基础查询条件
|
||||
// 获取登录者信息
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
Long userId = loginUser.getUserId();
|
||||
Integer tenantId = loginUser.getTenantId().intValue();
|
||||
|
||||
// 先构建基础查询条件
|
||||
QueryWrapper<PatientBaseInfoDto> queryWrapper = HisQueryUtils.buildQueryWrapper(
|
||||
patientBaseInfoDto, searchKey, new HashSet<>(Arrays.asList(CommonConstants.FieldName.Name,
|
||||
CommonConstants.FieldName.BusNo, CommonConstants.FieldName.PyStr, CommonConstants.FieldName.WbStr)),
|
||||
request);
|
||||
|
||||
// 检查是否是精确ID查询(从门诊挂号页面跳转时使用)
|
||||
boolean hasExactIdQuery = (patientBaseInfoDto.getId() != null);
|
||||
|
||||
// 只有非精确ID查询时,才添加医生患者过滤条件
|
||||
if (!hasExactIdQuery) {
|
||||
// 查询当前用户对应的医生信息
|
||||
LambdaQueryWrapper<com.openhis.administration.domain.Practitioner> practitionerQuery = new LambdaQueryWrapper<>();
|
||||
practitionerQuery.eq(com.openhis.administration.domain.Practitioner::getUserId, userId);
|
||||
// 使用list()避免TooManyResultsException异常,然后取第一个记录
|
||||
List<com.openhis.administration.domain.Practitioner> practitionerList = practitionerService.list(practitionerQuery);
|
||||
com.openhis.administration.domain.Practitioner practitioner = practitionerList != null && !practitionerList.isEmpty() ? practitionerList.get(0) : null;
|
||||
|
||||
// 如果当前用户是医生,添加医生患者过滤条件
|
||||
if (practitioner != null) {
|
||||
// 查询该医生作为接诊医生(ADMITTER, code="1")和挂号医生(REGISTRATION_DOCTOR, code="12")的所有就诊记录的患者ID
|
||||
List<Long> doctorPatientIds = patientManageMapper.getPatientIdsByPractitionerId(
|
||||
practitioner.getId(),
|
||||
Arrays.asList(ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode()),
|
||||
tenantId);
|
||||
|
||||
if (doctorPatientIds != null && !doctorPatientIds.isEmpty()) {
|
||||
// 添加患者ID过滤条件 - 注意:这里使用列名而不是表别名
|
||||
queryWrapper.in("id", doctorPatientIds);
|
||||
} else {
|
||||
// 如果没有相关患者,返回空结果
|
||||
queryWrapper.eq("id", -1); // 设置一个不存在的ID
|
||||
}
|
||||
}
|
||||
// 如果不是医生,查询所有患者
|
||||
}
|
||||
|
||||
IPage<PatientBaseInfoDto> patientInformationPage
|
||||
= patientManageMapper.getPatientPage(new Page<>(pageNo, pageSize), queryWrapper);
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.core.common.core.domain.R;
|
||||
import com.core.common.enums.DelFlag;
|
||||
import com.core.common.exception.ServiceException;
|
||||
import com.core.common.utils.AssignSeqUtil;
|
||||
import com.core.common.utils.MessageUtils;
|
||||
@@ -18,8 +17,6 @@ import com.openhis.common.constant.PromptMsgConstant;
|
||||
import com.openhis.common.enums.*;
|
||||
import com.openhis.document.domain.RequestForm;
|
||||
import com.openhis.document.service.IRequestFormService;
|
||||
import com.openhis.lab.domain.Specimen;
|
||||
import com.openhis.lab.service.ISpecimenService;
|
||||
import com.openhis.web.doctorstation.dto.ActivityChildrenJsonParams;
|
||||
import com.openhis.web.doctorstation.utils.AdviceUtils;
|
||||
import com.openhis.web.regdoctorstation.appservice.IRequestFormManageAppService;
|
||||
@@ -70,39 +67,6 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
@Resource
|
||||
IActivityDefinitionService iActivityDefinitionService;
|
||||
|
||||
@Resource
|
||||
ISpecimenService iSpecimenService;
|
||||
|
||||
/**
|
||||
* 校验当前用户是否有权操作该申请单(申请者本人或管理员)
|
||||
*/
|
||||
private R<?> validateRequestFormPermission(RequestForm requestForm) {
|
||||
if (SecurityUtils.isAdmin(SecurityUtils.getUserId())) {
|
||||
return null;
|
||||
}
|
||||
Long currentPractitionerId = SecurityUtils.getLoginUser().getPractitionerId();
|
||||
Long requesterId = requestForm.getRequesterId();
|
||||
if (currentPractitionerId == null || requesterId == null
|
||||
|| !currentPractitionerId.equals(requesterId)) {
|
||||
return R.fail("无操作权限,仅申请开立者或管理员可操作");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验关联医嘱是否已采证(存在已采集/已接收标本则不可撤回)
|
||||
*/
|
||||
private boolean hasCollectedSpecimen(List<Long> serviceRequestIds) {
|
||||
if (serviceRequestIds == null || serviceRequestIds.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
long count = iSpecimenService.count(
|
||||
new LambdaQueryWrapper<Specimen>()
|
||||
.in(Specimen::getServiceId, serviceRequestIds)
|
||||
.ge(Specimen::getCollectionStatusEnum, SpecCollectStatus.COLLECTED.getValue()));
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存申请单
|
||||
*
|
||||
@@ -564,17 +528,12 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
if (requestForm == null) {
|
||||
return R.fail("申请单不存在");
|
||||
}
|
||||
R<?> permissionResult = validateRequestFormPermission(requestForm);
|
||||
if (permissionResult != null) {
|
||||
return permissionResult;
|
||||
}
|
||||
String prescriptionNo = requestForm.getPrescriptionNo();
|
||||
|
||||
// 查询该申请单下所有 ServiceRequest(含子项)
|
||||
List<ServiceRequest> serviceRequests = iServiceRequestService.list(
|
||||
new LambdaQueryWrapper<ServiceRequest>()
|
||||
.eq(ServiceRequest::getPrescriptionNo, prescriptionNo)
|
||||
.eq(ServiceRequest::getDeleteFlag, DelFlag.NO.getCode()));
|
||||
.eq(ServiceRequest::getPrescriptionNo, prescriptionNo));
|
||||
if (serviceRequests == null || serviceRequests.isEmpty()) {
|
||||
return R.fail("未找到关联的诊疗医嘱");
|
||||
}
|
||||
@@ -604,7 +563,7 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
// 4. 删除申请单
|
||||
iRequestFormService.removeById(requestFormId);
|
||||
|
||||
log.info("检验申请单删除成功,requestFormId={}, prescriptionNo={}", requestFormId, prescriptionNo);
|
||||
log.info("检查申请单删除成功,requestFormId={}, prescriptionNo={}", requestFormId, prescriptionNo);
|
||||
return R.ok("删除成功");
|
||||
}
|
||||
|
||||
@@ -617,47 +576,32 @@ public class RequestFormManageAppServiceImpl implements IRequestFormManageAppSer
|
||||
if (requestForm == null) {
|
||||
return R.fail("申请单不存在");
|
||||
}
|
||||
R<?> permissionResult = validateRequestFormPermission(requestForm);
|
||||
if (permissionResult != null) {
|
||||
return permissionResult;
|
||||
}
|
||||
String prescriptionNo = requestForm.getPrescriptionNo();
|
||||
|
||||
// 查询该申请单下所有 ServiceRequest
|
||||
List<ServiceRequest> serviceRequests = iServiceRequestService.list(
|
||||
new LambdaQueryWrapper<ServiceRequest>()
|
||||
.eq(ServiceRequest::getPrescriptionNo, prescriptionNo)
|
||||
.eq(ServiceRequest::getDeleteFlag, DelFlag.NO.getCode()));
|
||||
.eq(ServiceRequest::getPrescriptionNo, prescriptionNo));
|
||||
if (serviceRequests == null || serviceRequests.isEmpty()) {
|
||||
return R.fail("未找到关联的诊疗医嘱");
|
||||
}
|
||||
|
||||
// 校验:只有已签发(status=2)的申请单可撤回
|
||||
boolean allActive = serviceRequests.stream()
|
||||
.allMatch(sr -> RequestStatus.ACTIVE.getValue().equals(sr.getStatusEnum()));
|
||||
if (!allActive) {
|
||||
return R.fail("只有已签发状态的申请单可撤回");
|
||||
}
|
||||
|
||||
// 将所有 ServiceRequest 状态改回待签发(DRAFT=0)
|
||||
List<Long> serviceRequestIds = serviceRequests.stream()
|
||||
.map(ServiceRequest::getId).collect(Collectors.toList());
|
||||
|
||||
// 校验:标本已采集则不可撤回
|
||||
if (hasCollectedSpecimen(serviceRequestIds)) {
|
||||
return R.fail("标本已采集,无法撤回");
|
||||
}
|
||||
|
||||
// 校验:任一ServiceRequest为ACTIVE(status=2)即可撤回,与SQL的EXISTS逻辑一致
|
||||
boolean hasActive = serviceRequests.stream()
|
||||
.anyMatch(sr -> RequestStatus.ACTIVE.getValue().equals(sr.getStatusEnum()));
|
||||
if (!hasActive) {
|
||||
return R.fail("只有已签发且未采证的申请单可撤回");
|
||||
}
|
||||
|
||||
// 将所有已签发的 ServiceRequest 状态改回待签发,与申请单展示状态同步
|
||||
boolean updated = iServiceRequestService.update(
|
||||
iServiceRequestService.update(
|
||||
new ServiceRequest().setStatusEnum(RequestStatus.DRAFT.getValue()),
|
||||
new LambdaUpdateWrapper<ServiceRequest>()
|
||||
.in(ServiceRequest::getId, serviceRequestIds)
|
||||
.eq(ServiceRequest::getStatusEnum, RequestStatus.ACTIVE.getValue()));
|
||||
if (!updated) {
|
||||
return R.fail("撤回失败,医嘱状态已变更,请刷新后重试");
|
||||
}
|
||||
.in(ServiceRequest::getId, serviceRequestIds));
|
||||
|
||||
log.info("检验申请单撤回成功,requestFormId={}, prescriptionNo={}", requestFormId, prescriptionNo);
|
||||
log.info("检查申请单撤回成功,requestFormId={}, prescriptionNo={}", requestFormId, prescriptionNo);
|
||||
return R.ok("撤回成功");
|
||||
}
|
||||
|
||||
|
||||
@@ -194,8 +194,8 @@ public class RequestFormManageController {
|
||||
* @return 结果
|
||||
*/
|
||||
@PostMapping(value = "/delete")
|
||||
public R<?> deleteRequestForm(@RequestBody Map<String, Object> data) {
|
||||
return iRequestFormManageAppService.deleteRequestForm(parseLong(data.get("requestFormId")));
|
||||
public R<?> deleteRequestForm(@RequestBody Map<String, Long> data) {
|
||||
return iRequestFormManageAppService.deleteRequestForm(data.get("requestFormId"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,24 +205,7 @@ public class RequestFormManageController {
|
||||
* @return 结果
|
||||
*/
|
||||
@PostMapping(value = "/withdraw")
|
||||
public R<?> withdrawRequestForm(@RequestBody Map<String, Object> data) {
|
||||
return iRequestFormManageAppService.withdrawRequestForm(parseLong(data.get("requestFormId")));
|
||||
}
|
||||
|
||||
private Long parseLong(Object value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof Long) {
|
||||
return (Long) value;
|
||||
}
|
||||
if (value instanceof Number) {
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(value.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
public R<?> withdrawRequestForm(@RequestBody Map<String, Long> data) {
|
||||
return iRequestFormManageAppService.withdrawRequestForm(data.get("requestFormId"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ public class HomeController {
|
||||
HomeStatisticsDto statisticsDto = homeStatisticsService.getHomeStatistics();
|
||||
|
||||
// 获取待写病历数量
|
||||
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
|
||||
R<?> pendingEmrCount = doctorStationEmrAppService.getPendingEmrCount(practitionerId, null);
|
||||
Long userId = SecurityUtils.getLoginUser().getUserId();
|
||||
R<?> pendingEmrCount = doctorStationEmrAppService.getPendingEmrCount(userId);
|
||||
|
||||
// 将待写病历数量添加到统计数据中
|
||||
statisticsDto.setPendingEmr((Integer) pendingEmrCount.getData());
|
||||
|
||||
@@ -74,6 +74,7 @@ public class TriageQueueAppServiceImpl implements TriageQueueAppService {
|
||||
.eq(TriageQueueItem::getTenantId, tenantId)
|
||||
.eq(TriageQueueItem::getQueueDate, qd)
|
||||
.eq(TriageQueueItem::getDeleteFlag, "0")
|
||||
.ne(TriageQueueItem::getStatus, TriageQueueStatus.COMPLETED.getValue())
|
||||
.orderByAsc(TriageQueueItem::getQueueOrder);
|
||||
|
||||
// 如果指定了科室,按科室过滤;否则查询所有科室(全科模式)
|
||||
@@ -91,6 +92,14 @@ public class TriageQueueAppServiceImpl implements TriageQueueAppService {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 双重保险:再次过滤掉 COMPLETED 状态的患者(防止数据库中有异常数据)
|
||||
if (list != null && !list.isEmpty()) {
|
||||
int beforeSize = list.size();
|
||||
list = list.stream()
|
||||
.filter(item -> !TriageQueueStatus.COMPLETED.getValue().equals(item.getStatus()))
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
}
|
||||
return R.ok(list);
|
||||
}
|
||||
|
||||
|
||||
@@ -894,9 +894,10 @@
|
||||
AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%')
|
||||
</if>
|
||||
ORDER BY t1.ID, t1.name ASC, t2.ID ASC
|
||||
LIMIT #{limit} OFFSET #{offset}
|
||||
</select>
|
||||
|
||||
<!-- 检查/检验项目专用分页查询:仅查指定 category_code + 定价,无库存/草稿库存/取药科室等无关逻辑 -->
|
||||
<!-- 检查项目专用分页查询:仅查检查(23) + 定价,无库存/草稿库存/取药科室等无关逻辑 -->
|
||||
<select id="getExaminationPage" resultType="com.openhis.web.doctorstation.dto.SurgeryItemDto">
|
||||
SELECT DISTINCT ON (t1.ID)
|
||||
t1.ID AS advice_definition_id,
|
||||
@@ -918,7 +919,7 @@
|
||||
ON t3.id = t1.org_id
|
||||
AND t3.delete_flag = '0'
|
||||
WHERE t1.delete_flag = '0'
|
||||
AND t1.category_code = #{categoryCode}
|
||||
AND t1.category_code = '23'
|
||||
<if test="searchKey != null and searchKey != ''">
|
||||
AND (t1.name ILIKE '%' || #{searchKey} || '%' OR t1.py_str ILIKE '%' || #{searchKey} || '%')
|
||||
</if>
|
||||
|
||||
@@ -4,38 +4,4 @@
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.openhis.web.doctorstation.mapper.DoctorStationEmrAppMapper">
|
||||
|
||||
<select id="getPendingEmrList" resultType="java.util.HashMap">
|
||||
SELECT e.id AS "encounterId",
|
||||
e.patient_id AS "patientId",
|
||||
p.name AS "patientName",
|
||||
p.gender_enum AS "gender",
|
||||
p.birth_date AS "birthDate",
|
||||
e.create_time AS "registerTime",
|
||||
e.bus_no AS "busNo"
|
||||
FROM adm_encounter e
|
||||
INNER JOIN adm_encounter_participant ep ON e.id = ep.encounter_id AND ep.practitioner_id = #{doctorId}
|
||||
LEFT JOIN adm_patient p ON e.patient_id = p.id
|
||||
LEFT JOIN doc_emr emr ON e.id = emr.encounter_id
|
||||
WHERE e.status_enum = 2
|
||||
AND emr.id IS NULL
|
||||
<if test="patientName != null and patientName != ''">
|
||||
AND p.name LIKE CONCAT('%', #{patientName}, '%')
|
||||
</if>
|
||||
ORDER BY e.create_time DESC
|
||||
</select>
|
||||
|
||||
<select id="getPendingEmrCount" resultType="java.lang.Long">
|
||||
SELECT COUNT(*)
|
||||
FROM adm_encounter e
|
||||
INNER JOIN adm_encounter_participant ep ON e.id = ep.encounter_id AND ep.practitioner_id = #{doctorId}
|
||||
LEFT JOIN doc_emr emr ON e.id = emr.encounter_id
|
||||
WHERE e.status_enum = 2
|
||||
AND emr.id IS NULL
|
||||
<if test="patientName != null and patientName != ''">
|
||||
AND e.patient_id IN (
|
||||
SELECT id FROM adm_patient WHERE name LIKE CONCAT('%', #{patientName}, '%')
|
||||
)
|
||||
</if>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -35,27 +35,21 @@
|
||||
WHERE ws.prescription_no = drf.prescription_no AND ws.delete_flag = '0'
|
||||
AND ws.status_enum = 8
|
||||
) THEN 6
|
||||
WHEN EXISTS (
|
||||
SELECT 1 FROM wor_service_request ws
|
||||
WHERE ws.prescription_no = drf.prescription_no AND ws.delete_flag = '0'
|
||||
AND ws.status_enum = 5
|
||||
) THEN 7
|
||||
WHEN EXISTS (
|
||||
SELECT 1 FROM wor_service_request ws
|
||||
WHERE ws.prescription_no = drf.prescription_no AND ws.delete_flag = '0'
|
||||
AND ws.status_enum = 3
|
||||
) THEN 5
|
||||
WHEN EXISTS (
|
||||
SELECT 1 FROM wor_service_request ws
|
||||
INNER JOIN lab_specimen ls ON ls.service_id = ws.id
|
||||
WHERE ws.prescription_no = drf.prescription_no AND ws.delete_flag = '0'
|
||||
AND ls.collection_status_enum >= 1
|
||||
) THEN 4
|
||||
WHEN EXISTS (
|
||||
SELECT 1 FROM wor_service_request ws
|
||||
WHERE ws.prescription_no = drf.prescription_no AND ws.delete_flag = '0'
|
||||
AND ws.status_enum = 2
|
||||
) THEN 1
|
||||
WHEN EXISTS (
|
||||
SELECT 1 FROM wor_service_request ws
|
||||
WHERE ws.prescription_no = drf.prescription_no AND ws.delete_flag = '0'
|
||||
AND ws.status_enum = 5
|
||||
) THEN 7
|
||||
ELSE 0
|
||||
END AS computed_status
|
||||
FROM doc_request_form AS drf
|
||||
|
||||
@@ -220,18 +220,3 @@ export function getSlotStatusDescription(value) {
|
||||
export function getSlotStatusClass(status) {
|
||||
return SlotStatusClassMap[status] || 'status-unbooked';
|
||||
}
|
||||
|
||||
/**
|
||||
* 诊疗项目分类代码(对应后端 ActivityDefCategory 枚举)
|
||||
* wor_activity_definition.category_code 字段
|
||||
*/
|
||||
export const ActivityCategory = {
|
||||
/** 治疗 */
|
||||
TREATMENT: '21',
|
||||
/** 检验 */
|
||||
PROOF: '22',
|
||||
/** 检查 */
|
||||
TEST: '23',
|
||||
/** 手术 */
|
||||
PROCEDURE: '24',
|
||||
};
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form
|
||||
v-show="showSearch"
|
||||
ref="queryRef"
|
||||
:model="queryParams"
|
||||
ref="queryRef"
|
||||
:inline="true"
|
||||
v-show="showSearch"
|
||||
label-width="90px"
|
||||
>
|
||||
<el-form-item label="查询日期">
|
||||
<el-form-item label="查询日期:">
|
||||
<el-date-picker
|
||||
v-model="queryTime"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
style="width: 300px"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 300px; margin-right: 20px"
|
||||
@change="getValue"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="费用性质">
|
||||
<el-form-item label="费用性质:">
|
||||
<el-select
|
||||
v-model="contractNo"
|
||||
placeholder="费用性质"
|
||||
clearable
|
||||
style="width: 160px"
|
||||
@change="getValue"
|
||||
style="width: 150px; margin-right: 30px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in contractList"
|
||||
@@ -33,241 +33,228 @@
|
||||
:value="item.busNo"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button type="primary" plain icon="Search" @click="getValue">查询</el-button>
|
||||
<el-button type="primary" plain icon="Printer" @click="print">打印</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item class="search-buttons">
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Search"
|
||||
@click="getValue"
|
||||
<!-- <el-form-item label="科室:" prop="sourceLocationId">
|
||||
<el-select
|
||||
v-model="queryParams.sourceLocationId"
|
||||
placeholder=""
|
||||
clearable
|
||||
style="width: 150px"
|
||||
>
|
||||
查询
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
icon="Printer"
|
||||
@click="print"
|
||||
>
|
||||
打印
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-option
|
||||
v-for="issueDepartment in issueDepartmentDto"
|
||||
:key="issueDepartment.id"
|
||||
:label="issueDepartment.name"
|
||||
:value="issueDepartment.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
|
||||
<div
|
||||
v-loading="loading"
|
||||
class="report-container"
|
||||
>
|
||||
<div class="report-title">
|
||||
门诊收费日结单
|
||||
</div>
|
||||
|
||||
<el-row :gutter="20" class="info-row">
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="info-cell">
|
||||
<span class="info-label">经办人姓名:</span>
|
||||
<span class="info-value">{{ userStore.nickName || '全部' }}</span>
|
||||
</div>
|
||||
<!-- <el-row :gutter="10" class="mb8">
|
||||
<el-col :span="1.5">
|
||||
<el-button type="primary" plain icon="Search" @click="getValue">查询</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button type="warning" plain icon="CircleClose" @click="handleClear">重置</el-button>
|
||||
</el-col>
|
||||
</el-row> -->
|
||||
<div v-loading="loading">
|
||||
<el-row
|
||||
:gutter="10"
|
||||
outpatientNo="mb8"
|
||||
style="
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0 20px;
|
||||
"
|
||||
>
|
||||
<!-- <el-col :span="3">
|
||||
<span>经办人编号:</span>
|
||||
</el-col> -->
|
||||
<el-col :span="3">
|
||||
<span class="label">经办人姓名:</span>
|
||||
<span class="value"> {{ userStore.nickName }}</span>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="info-cell">
|
||||
<span class="info-label">科室:</span>
|
||||
<span class="info-value">{{ userStore.orgName || '-' }}</span>
|
||||
</div>
|
||||
<el-col :span="3">
|
||||
<span class="label">科室:</span>
|
||||
<span class="value">{{ userStore.orgName }}</span>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="info-cell">
|
||||
<span class="info-label">机构:</span>
|
||||
<span class="info-value">{{ userStore.hospitalName || '-' }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="info-cell">
|
||||
<span class="info-label">时间:</span>
|
||||
<span class="info-value">{{ queryTime && queryTime.length === 2 ? queryTime[0] + ' ~ ' + queryTime[1] : '-' }}</span>
|
||||
</div>
|
||||
<el-col :span="4.5">
|
||||
<span class="label">时间:</span>
|
||||
<span class="value"> {{ queryTime[0] + '~' + queryTime[1] }} </span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="section-title">收入汇总</div>
|
||||
<el-row :gutter="16" class="data-row">
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">总收入:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.cashSum) }}</span>
|
||||
</div>
|
||||
<el-row
|
||||
:gutter="10"
|
||||
outpatientNo="mb8"
|
||||
style="
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0 20px;
|
||||
"
|
||||
>
|
||||
<el-col :span="3">
|
||||
<span class="label">实际现金收入:</span>
|
||||
<span class="value"> {{ formatValue(reportValue.cashSum) }}</span>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">现金:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.rmbCashSum) }}</span>
|
||||
</div>
|
||||
<el-col :span="3">
|
||||
<span class="label">现金:</span>
|
||||
<span class="value">{{ formatValue(reportValue.rmbCashSum) }}</span>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">微信:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.vxCashSum) }}</span>
|
||||
</div>
|
||||
<el-col :span="3">
|
||||
<span class="label">微信:</span>
|
||||
<span class="value">{{ formatValue(reportValue.vxCashSum) }}</span>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">支付宝:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.aliCashSum) }}</span>
|
||||
</div>
|
||||
<el-col :span="3">
|
||||
<span class="label">支付宝:</span>
|
||||
<span class="value">{{ formatValue(reportValue.aliCashSum) }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="section-title">医保支付</div>
|
||||
<el-row :gutter="16" class="data-row">
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">统筹支付:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.tcSum) }}</span>
|
||||
</div>
|
||||
<el-row
|
||||
:gutter="10"
|
||||
outpatientNo="mb8"
|
||||
style="
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0 20px;
|
||||
"
|
||||
>
|
||||
<el-col :span="3">
|
||||
<span class="label">统筹支付:</span>
|
||||
<span class="value">{{ formatValue(reportValue.tcSum) }}</span>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">账户支付:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.zhSum) }}</span>
|
||||
</div>
|
||||
<el-col :span="3">
|
||||
<span class="label">账户支付:</span>
|
||||
<span class="value">{{ formatValue(reportValue.zhSum) }}</span>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">基金支付总额:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.fundSum) }}</span>
|
||||
</div>
|
||||
<el-col :span="3">
|
||||
<span class="label">基金支付总额:</span>
|
||||
<span class="value">{{ formatValue(reportValue.fundSum) }}</span>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">医保统筹+账户:</span>
|
||||
<span class="data-value">{{ formatValue(Number(reportValue.zhSum || 0) + Number(reportValue.fundSum || 0)) }}</span>
|
||||
</div>
|
||||
<!-- <el-col :span="3">
|
||||
<span>医保人次:{{ reportValue.aliCashSum }}</span>
|
||||
</el-col> -->
|
||||
</el-row>
|
||||
<el-row
|
||||
:gutter="10"
|
||||
outpatientNo="mb8"
|
||||
style="
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0 20px;
|
||||
"
|
||||
>
|
||||
<el-col :span="3">
|
||||
<span class="label">诊查费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.DIAGNOSTIC_FEE) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<span class="label">检查费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.CHECK_FEE) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<span class="label">化验费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.DIAGNOSTIC_TEST_FEE) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<span class="label">治疗费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.MEDICAL_EXPENSE_FEE) }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider />
|
||||
|
||||
<div class="section-title">费用明细</div>
|
||||
<el-row :gutter="16" class="data-row">
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">诊查费:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.DIAGNOSTIC_FEE) }}</span>
|
||||
</div>
|
||||
<el-row
|
||||
:gutter="10"
|
||||
outpatientNo="mb8"
|
||||
style="
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0 20px;
|
||||
"
|
||||
>
|
||||
<el-col :span="3">
|
||||
<span class="label">西药费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.WEST_MEDICINE) }}</span>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">检查费:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.CHECK_FEE) }}</span>
|
||||
</div>
|
||||
<el-col :span="3">
|
||||
<span class="label">中药饮片费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.CHINESE_MEDICINE_SLICES_FEE) }}</span>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">化验费:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.DIAGNOSTIC_TEST_FEE) }}</span>
|
||||
</div>
|
||||
<el-col :span="3">
|
||||
<span class="label">中成药费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.CHINESE_MEDICINE_FEE) }}</span>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">治疗费:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.MEDICAL_EXPENSE_FEE) }}</span>
|
||||
</div>
|
||||
<el-col :span="3">
|
||||
<span class="label">卫生材料费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.SANITARY_MATERIALS_FEE) }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16" class="data-row">
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">西药费:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.WEST_MEDICINE) }}</span>
|
||||
</div>
|
||||
<el-row
|
||||
:gutter="10"
|
||||
outpatientNo="mb8"
|
||||
style="
|
||||
margin: 20px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0 20px;
|
||||
"
|
||||
>
|
||||
<el-col :span="3">
|
||||
<span class="label">诊疗费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.GENERAL_CONSULTATION_FEE) }}</span>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">中药饮片费:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.CHINESE_MEDICINE_SLICES_FEE) }}</span>
|
||||
</div>
|
||||
<el-col :span="3">
|
||||
<span class="label">挂号费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.REGISTRATION_FEE) }}</span>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">中成药费:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.CHINESE_MEDICINE_FEE) }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">卫生材料费:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.SANITARY_MATERIALS_FEE) }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16" class="data-row">
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">普通挂号费:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.GENERAL_CONSULTATION_FEE) }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">挂号费:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.REGISTRATION_FEE) }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">其他费用:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.OTHER_FEE) }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell">
|
||||
<span class="data-label">退费金额:</span>
|
||||
<span class="data-value">{{ formatValue(reportValue.returnFee) }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16" class="data-row summary-row">
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell summary-cell">
|
||||
<span class="data-label summary-label">费用总额:</span>
|
||||
<span class="data-value value-highlight">{{ totalFeeAmount }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<div class="data-cell summary-cell">
|
||||
<span class="data-label summary-label">医保报销:</span>
|
||||
<span class="data-value value-highlight">{{ insuranceReimbursement }}</span>
|
||||
</div>
|
||||
<el-col :span="3">
|
||||
<span class="label">其他费用:</span>
|
||||
<span class="value">{{ formatValue(reportValue.OTHER_FEE) }}</span>
|
||||
</el-col>
|
||||
<!-- <el-col :span="3">
|
||||
<span>现金:</span>
|
||||
</el-col> -->
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="DayEnd">
|
||||
import { ref, reactive, toRefs, getCurrentInstance, computed } from 'vue';
|
||||
import Decimal from 'decimal.js';
|
||||
import { getTotal, getContractList, getRreportReturnIssue } from './component/api';
|
||||
<script setup name="dayEnd">
|
||||
import {getContractList, getRreportReturnIssue, getTotal} from './component/api';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import { formatDateStr } from '@/utils/index';
|
||||
import {formatDateStr} from '@/utils/index';
|
||||
import Decimal from 'decimal.js';
|
||||
|
||||
const userStore = useUserStore();
|
||||
// import Dialog from "./components/Dialog";
|
||||
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const purchaseinventoryRef = ref(null); // 初始化 ref
|
||||
const purchaseinventoryList = ref([]);
|
||||
const reportValue = ref({});
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
const open = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const occurrenceTime = ref([]);
|
||||
const contractList = ref([]);
|
||||
const total = ref(0);
|
||||
const title = ref('');
|
||||
const contractList = ref(undefined);
|
||||
const reportValue = ref({});
|
||||
const queryTime = ref([
|
||||
formatDateStr(new Date(), 'YYYY-MM-DD'),
|
||||
formatDateStr(new Date(), 'YYYY-MM-DD'),
|
||||
@@ -313,6 +300,10 @@ function getContract() {
|
||||
}
|
||||
|
||||
function getPharmacyCabinetLists() {
|
||||
// occurrenceTime.value =
|
||||
// getDepartmentList().then((response) => {
|
||||
// issueDepartmentDto.value = response.data
|
||||
// })
|
||||
}
|
||||
/** 查询调拨管理项目列表 */
|
||||
function getList() {
|
||||
@@ -340,6 +331,7 @@ function handleQuery() {
|
||||
|
||||
/** 清空条件按钮操作 */
|
||||
function handleClear() {
|
||||
// 清空查询条件
|
||||
queryParams.value.approvalTimeSTime = '';
|
||||
queryParams.value.approvalTimeETime = '';
|
||||
occurrenceTime.value = '';
|
||||
@@ -356,11 +348,12 @@ function handleSelectionChange(selection) {
|
||||
|
||||
/** 打印门诊日结 */
|
||||
async function print() {
|
||||
// const selectedRows = proxy.$refs['tableRef'].getSelectionRows();
|
||||
console.log(reportValue.value, '==reportValue.value==');
|
||||
const result = {
|
||||
data: [
|
||||
{
|
||||
...reportValue.value,
|
||||
...reportValue.value, // 将 reportValue.value 中的所有属性展开到 result 中
|
||||
nickName: userStore.nickName,
|
||||
orgName: userStore.orgName,
|
||||
fixmedinsName: userStore.hospitalName,
|
||||
@@ -381,12 +374,14 @@ async function print() {
|
||||
],
|
||||
};
|
||||
console.log(result, '==result.data==');
|
||||
// 将对象转换为 JSON 字符串
|
||||
let jsonString = JSON.stringify(result, null, 2);
|
||||
console.log(jsonString, 'jsonstring');
|
||||
await CefSharp.BindObjectAsync('boundAsync');
|
||||
await boundAsync
|
||||
.printReport(getPrintFileName(contractNo.value), jsonString)
|
||||
.then((response) => {
|
||||
//返回结果是jsonString,可判断其调用是否成功
|
||||
console.log(response, 'response');
|
||||
var res = JSON.parse(response);
|
||||
if (!res.IsSuccess) {
|
||||
@@ -402,9 +397,9 @@ function getPrintFileName(value) {
|
||||
switch (value) {
|
||||
case '0000':
|
||||
return '门诊日结单(按登录角色查询)自费.grf';
|
||||
case '229900':
|
||||
case '229900': // 省医保
|
||||
return '门诊日结单(按登录角色查询)省医保.grf';
|
||||
case '220100':
|
||||
case '220100': // 市医保
|
||||
return '门诊日结单(按登录角色查询)市医保.grf';
|
||||
}
|
||||
}
|
||||
@@ -413,173 +408,28 @@ function formatValue(value) {
|
||||
return value == null || value == undefined ? '0.00 元' : value.toFixed(2) + ' 元';
|
||||
}
|
||||
|
||||
// 计算属性:费用总额
|
||||
const totalFeeAmount = computed(() => {
|
||||
const v = reportValue.value;
|
||||
const sum =
|
||||
Number(v.DIAGNOSTIC_FEE || 0) +
|
||||
Number(v.CHECK_FEE || 0) +
|
||||
Number(v.DIAGNOSTIC_TEST_FEE || 0) +
|
||||
Number(v.MEDICAL_EXPENSE_FEE || 0) +
|
||||
Number(v.WEST_MEDICINE || 0) +
|
||||
Number(v.CHINESE_MEDICINE_SLICES_FEE || 0) +
|
||||
Number(v.CHINESE_MEDICINE_FEE || 0) +
|
||||
Number(v.GENERAL_CONSULTATION_FEE || 0) +
|
||||
Number(v.REGISTRATION_FEE || 0) +
|
||||
Number(v.OTHER_FEE || 0) +
|
||||
Number(v.SANITARY_MATERIALS_FEE || 0);
|
||||
return formatValue(sum);
|
||||
});
|
||||
|
||||
// 计算属性:医保报销(统筹+账户)
|
||||
const insuranceReimbursement = computed(() => {
|
||||
const v = reportValue.value;
|
||||
const sum = Number(v.tcSum || 0) + Number(v.zhSum || 0);
|
||||
return formatValue(sum);
|
||||
});
|
||||
|
||||
getList();
|
||||
getPharmacyCabinetLists();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.report-container {
|
||||
max-width: 1200px;
|
||||
margin: 16px auto;
|
||||
padding: 24px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.report-title {
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 20px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
padding: 12px 0;
|
||||
}
|
||||
|
||||
.info-cell {
|
||||
.custom-tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6px 0;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
min-width: 80px;
|
||||
.title {
|
||||
font-weight: bold;
|
||||
font-size: large;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
flex: 1;
|
||||
.label {
|
||||
display: inline-block;
|
||||
width: 120px !important;
|
||||
}
|
||||
|
||||
.data-row {
|
||||
padding: 4px 0;
|
||||
.value {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.data-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
margin-bottom: 4px;
|
||||
background: #fafafa;
|
||||
border-radius: 4px;
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.data-label {
|
||||
color: #606266;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
min-width: 100px;
|
||||
text-align: right;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.data-value {
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.value-highlight {
|
||||
color: #409eff;
|
||||
font-weight: 700;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.summary-row {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.summary-cell {
|
||||
background: #ecf5ff;
|
||||
border: 1px solid #d9ecff;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
padding: 8px 0 8px 12px;
|
||||
margin: 8px 0 4px;
|
||||
border-left: 3px solid #409eff;
|
||||
background: linear-gradient(90deg, rgba(64, 158, 255, 0.05) 0%, transparent 100%);
|
||||
}
|
||||
|
||||
.search-buttons {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.search-buttons .el-form-item__content {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
:deep(.el-divider--horizontal) {
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.el-form--inline .el-form-item {
|
||||
margin-bottom: 12px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.data-label {
|
||||
min-width: 80px;
|
||||
text-align: left;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.data-value {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
min-width: 70px;
|
||||
}
|
||||
.el-col {
|
||||
margin-right: 50px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,352 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form
|
||||
:model="queryParams"
|
||||
ref="queryRef"
|
||||
:inline="true"
|
||||
v-show="showSearch"
|
||||
label-width="90px"
|
||||
>
|
||||
<el-form-item label="查询日期:">
|
||||
<el-date-picker
|
||||
v-model="queryTime"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
style="width: 300px; margin-right: 20px"
|
||||
@change="getValue"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="费用性质:">
|
||||
<el-select
|
||||
v-model="contractNo"
|
||||
placeholder="费用性质"
|
||||
clearable
|
||||
@change="getValue"
|
||||
style="width: 150px; margin-right: 30px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in contractList"
|
||||
:key="item.busNo"
|
||||
:label="item.contractName"
|
||||
:value="item.busNo"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button type="primary" plain icon="Search" @click="getValue">查询</el-button>
|
||||
<el-button type="primary" plain icon="Printer" @click="print">打印</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div v-loading="loading" style="width: 1300px">
|
||||
<div style="text-align: center">
|
||||
<h2>门诊收费日结单</h2>
|
||||
</div>
|
||||
<el-row
|
||||
:gutter="5"
|
||||
style="margin: 20px 0; display: flex; align-items: center; justify-content: flex-start; padding: 0 20px"
|
||||
>
|
||||
<el-col :span="4">
|
||||
<span class="label">经办人姓名:</span>
|
||||
<span class="value">{{ userStore.nickName }}</span>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<span class="label">科室:</span>
|
||||
<span class="value">{{ userStore.orgName }}</span>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<span class="label">时间:</span>
|
||||
<span class="value">{{ queryTime[0] + '~' + queryTime[1] }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="divider"></div>
|
||||
<el-row
|
||||
:gutter="10"
|
||||
style="margin: 20px 0; display: flex; align-items: center; justify-content: flex-start; padding: 0 20px"
|
||||
>
|
||||
<el-col :span="5">
|
||||
<span class="label">总收入:</span>
|
||||
<span class="value">{{ formatValue(reportValue.cashSum) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<span class="label">现金:</span>
|
||||
<span class="value">{{ formatValue(reportValue.rmbCashSum) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<span class="label">微信:</span>
|
||||
<span class="value">{{ formatValue(reportValue.vxCashSum) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<span class="label">支付宝:</span>
|
||||
<span class="value">{{ formatValue(reportValue.aliCashSum) }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="divider"></div>
|
||||
<el-row
|
||||
:gutter="10"
|
||||
style="margin: 20px 0; display: flex; align-items: center; justify-content: flex-start; padding: 0 20px"
|
||||
>
|
||||
<el-col :span="5">
|
||||
<span class="label">统筹支付:</span>
|
||||
<span class="value">{{ formatValue(reportValue.tcSum) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<span class="label">账户支付:</span>
|
||||
<span class="value">{{ formatValue(reportValue.zhSum) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<span class="label">基金支付总额:</span>
|
||||
<span class="value">{{ formatValue(reportValue.fundSum) }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="divider"></div>
|
||||
<el-row
|
||||
:gutter="10"
|
||||
style="margin: 20px 0; display: flex; align-items: center; justify-content: flex-start; padding: 0 20px"
|
||||
>
|
||||
<el-col :span="5">
|
||||
<span class="label">诊查费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.DIAGNOSTIC_FEE) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<span class="label">检查费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.CHECK_FEE) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<span class="label">化验费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.DIAGNOSTIC_TEST_FEE) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<span class="label">治疗费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.MEDICAL_EXPENSE_FEE) }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row
|
||||
:gutter="10"
|
||||
style="margin: 20px 0; display: flex; align-items: center; justify-content: flex-start; padding: 0 20px"
|
||||
>
|
||||
<el-col :span="5">
|
||||
<span class="label">西药费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.WEST_MEDICINE) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<span class="label">中药饮片费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.CHINESE_MEDICINE_SLICES_FEE) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<span class="label">中成药费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.CHINESE_MEDICINE_FEE) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<span class="label">卫生材料费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.SANITARY_MATERIALS_FEE) }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row
|
||||
:gutter="10"
|
||||
style="margin: 20px 0; display: flex; align-items: center; justify-content: flex-start; padding: 0 20px"
|
||||
>
|
||||
<el-col :span="5">
|
||||
<span class="label">诊疗费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.GENERAL_CONSULTATION_FEE) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<span class="label">挂号费:</span>
|
||||
<span class="value">{{ formatValue(reportValue.REGISTRATION_FEE) }}</span>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<span class="label">其他费用:</span>
|
||||
<span class="value">{{ formatValue(reportValue.OTHER_FEE) }}</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="dayEnd">
|
||||
import {getContractList, getRreportReturnIssue, getTotal} from './component/api';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import {formatDateStr} from '@/utils/index';
|
||||
import Decimal from 'decimal.js';
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const purchaseinventoryRef = ref(null);
|
||||
const purchaseinventoryList = ref([]);
|
||||
const open = ref(false);
|
||||
const loading = ref(true);
|
||||
const showSearch = ref(true);
|
||||
const ids = ref([]);
|
||||
const single = ref(true);
|
||||
const multiple = ref(true);
|
||||
const total = ref(0);
|
||||
const title = ref('');
|
||||
const contractList = ref(undefined);
|
||||
const reportValue = ref({});
|
||||
const queryTime = ref([
|
||||
formatDateStr(new Date(), 'YYYY-MM-DD'),
|
||||
formatDateStr(new Date(), 'YYYY-MM-DD'),
|
||||
]);
|
||||
const contractNo = ref('0000');
|
||||
|
||||
const data = reactive({
|
||||
queryParams: {
|
||||
form: {},
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
searchKey: undefined,
|
||||
purposeLocationId: undefined,
|
||||
sourceLocationId: undefined,
|
||||
supplierId: undefined,
|
||||
approvalTimeSTime: undefined,
|
||||
approvalTimeETime: undefined,
|
||||
},
|
||||
rules: {},
|
||||
});
|
||||
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
getValue();
|
||||
function getValue() {
|
||||
loading.value = true;
|
||||
getTotal({
|
||||
contractNo: contractNo.value,
|
||||
startTime: queryTime.value[0] + ' 00:00:00',
|
||||
endTime: queryTime.value[1] + ' 23:59:59',
|
||||
entererId: userStore.practitionerId,
|
||||
}).then((res) => {
|
||||
loading.value = false;
|
||||
reportValue.value = res.data;
|
||||
});
|
||||
}
|
||||
|
||||
getContract();
|
||||
function getContract() {
|
||||
getContractList().then((response) => {
|
||||
contractList.value = response.data;
|
||||
});
|
||||
}
|
||||
|
||||
function getPharmacyCabinetLists() {
|
||||
}
|
||||
/** 查询调拨管理项目列表 */
|
||||
function getList() {
|
||||
loading.value = true;
|
||||
getRreportReturnIssue(queryParams.value).then((res) => {
|
||||
loading.value = false;
|
||||
purchaseinventoryList.value = res.data.records;
|
||||
total.value = res.data.total;
|
||||
});
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
function handleQuery() {
|
||||
queryParams.value.approvalTimeSTime =
|
||||
occurrenceTime.value && occurrenceTime.value.length == 2
|
||||
? occurrenceTime.value[0] + ' 00:00:00'
|
||||
: '';
|
||||
queryParams.value.approvalTimeETime =
|
||||
occurrenceTime.value && occurrenceTime.value.length == 2
|
||||
? occurrenceTime.value[1] + ' 23:59:59'
|
||||
: '';
|
||||
queryParams.value.pageNo = 1;
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 清空条件按钮操作 */
|
||||
function handleClear() {
|
||||
queryParams.value.approvalTimeSTime = '';
|
||||
queryParams.value.approvalTimeETime = '';
|
||||
occurrenceTime.value = '';
|
||||
proxy.resetForm('queryRef');
|
||||
getList();
|
||||
}
|
||||
|
||||
/** 选择条数 */
|
||||
function handleSelectionChange(selection) {
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
}
|
||||
|
||||
/** 打印门诊日结 */
|
||||
async function print() {
|
||||
console.log(reportValue.value, '==reportValue.value==');
|
||||
const result = {
|
||||
data: [
|
||||
{
|
||||
...reportValue.value,
|
||||
nickName: userStore.nickName,
|
||||
orgName: userStore.orgName,
|
||||
fixmedinsName: userStore.hospitalName,
|
||||
queryTime: queryTime.value[0] + '~' + queryTime.value[1],
|
||||
zfAmount: new Decimal(reportValue.value.zhSum || 0).add(reportValue.value.fundSum || 0),
|
||||
feeAmount: new Decimal(reportValue.value.DIAGNOSTIC_FEE || 0)
|
||||
.add(reportValue.value.CHECK_FEE || 0)
|
||||
.add(reportValue.value.DIAGNOSTIC_TEST_FEE || 0)
|
||||
.add(reportValue.value.MEDICAL_EXPENSE_FEE || 0)
|
||||
.add(reportValue.value.WEST_MEDICINE || 0)
|
||||
.add(reportValue.value.CHINESE_MEDICINE_SLICES_FEE || 0)
|
||||
.add(reportValue.value.CHINESE_MEDICINE_FEE || 0)
|
||||
.add(reportValue.value.GENERAL_CONSULTATION_FEE || 0)
|
||||
.add(reportValue.value.REGISTRATION_FEE || 0)
|
||||
.add(reportValue.value.OTHER_FEE || 0)
|
||||
.add(reportValue.value.SANITARY_MATERIALS_FEE || 0),
|
||||
},
|
||||
],
|
||||
};
|
||||
console.log(result, '==result.data==');
|
||||
let jsonString = JSON.stringify(result, null, 2);
|
||||
console.log(jsonString, 'jsonstring');
|
||||
await CefSharp.BindObjectAsync('boundAsync');
|
||||
await boundAsync
|
||||
.printReport(getPrintFileName(contractNo.value), jsonString)
|
||||
.then((response) => {
|
||||
console.log(response, 'response');
|
||||
var res = JSON.parse(response);
|
||||
if (!res.IsSuccess) {
|
||||
proxy.$modal.msgError('调用打印插件失败:' + res.ErrorMessage);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
proxy.$modal.msgError('调用打印插件失败:' + error);
|
||||
});
|
||||
}
|
||||
|
||||
function getPrintFileName(value) {
|
||||
switch (value) {
|
||||
case '0000':
|
||||
return '门诊日结单(按登录角色查询)自费.grf';
|
||||
case '229900':
|
||||
return '门诊日结单(按登录角色查询)省医保.grf';
|
||||
case '220100':
|
||||
return '门诊日结单(按登录角色查询)市医保.grf';
|
||||
}
|
||||
}
|
||||
|
||||
function formatValue(value) {
|
||||
return value == null || value == undefined ? '0.00 元' : value.toFixed(2) + ' 元';
|
||||
}
|
||||
|
||||
getList();
|
||||
getPharmacyCabinetLists();
|
||||
</script>
|
||||
<style scoped>
|
||||
.label {
|
||||
display: inline-block;
|
||||
width: 120px !important;
|
||||
}
|
||||
.value {
|
||||
float: right;
|
||||
}
|
||||
.el-col {
|
||||
margin-right: 50px;
|
||||
}
|
||||
.divider {
|
||||
height: 3px;
|
||||
background-color: #000;
|
||||
margin: 20px 0;
|
||||
}
|
||||
</style>
|
||||
@@ -749,26 +749,22 @@ function handleInfectiousDiseaseReport() {
|
||||
'手足口病': '0311',
|
||||
};
|
||||
|
||||
// 获取所有命中传染病映射的诊断,但跳过已有已提交报卡的诊断
|
||||
const infectiousDiagnoses = form.value.diagnosisList
|
||||
.map(d => ({
|
||||
diagnosis: d,
|
||||
diseaseCode: d.name && d.hasInfectiousReport !== 1 ? diseaseNameToCode[d.name] : null
|
||||
}))
|
||||
.filter(item => item.diseaseCode);
|
||||
|
||||
const allSelectedDiseases = infectiousDiagnoses.map(item => item.diseaseCode);
|
||||
// 获取所有诊断名称对应的报卡编码,但跳过已有已提交报卡的诊断
|
||||
const allSelectedDiseases = form.value.diagnosisList
|
||||
.filter(d => d.name && d.hasInfectiousReport !== 1)
|
||||
.map(d => diseaseNameToCode[d.name] || null)
|
||||
.filter(code => code);
|
||||
|
||||
if (allSelectedDiseases.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 优先使用命中传染病映射的主诊断,否则使用第一条命中的传染病诊断
|
||||
const mainInfectiousDiagnosis = infectiousDiagnoses.find(item => item.diagnosis.maindiseFlag === 1)?.diagnosis;
|
||||
const firstInfectiousDiagnosis = infectiousDiagnoses[0].diagnosis;
|
||||
// 优先使用主诊断(同样跳过已有报卡的)
|
||||
const mainDiagnosis = form.value.diagnosisList.find(d => d.maindiseFlag === 1 && d.hasInfectiousReport !== 1);
|
||||
const firstDiagnosis = form.value.diagnosisList.find(d => d.hasInfectiousReport !== 1) || form.value.diagnosisList[0];
|
||||
|
||||
const diagnosisToShow = {
|
||||
...(mainInfectiousDiagnosis || firstInfectiousDiagnosis),
|
||||
...(mainDiagnosis || firstDiagnosis),
|
||||
selectedDiseases: allSelectedDiseases
|
||||
};
|
||||
|
||||
|
||||
@@ -1442,7 +1442,7 @@ async function buildSubmitData() {
|
||||
const submitData = {
|
||||
cardNo: formData.cardNo,
|
||||
visitId: props.patientInfo?.encounterId || formData.encounterId || null,
|
||||
diagId: formData.diagnosisId || null,
|
||||
diagId: formData.diagnosisId ? Number(formData.diagnosisId) : null,
|
||||
patId: formData.patientId || null,
|
||||
idType: 1, // 默认身份证
|
||||
idNo: formData.idNo,
|
||||
|
||||
@@ -397,165 +397,117 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:已选择(检查项目、检查方法为两类独立选择结果) -->
|
||||
<!-- 右侧:已选择 项目卡片(可展开显示检查方法) -->
|
||||
<div class="selected-panel">
|
||||
<div class="panel-label">已选择:</div>
|
||||
<div class="selected-tags">
|
||||
<template v-if="selectedItems.length === 0 && selectedMethods.length === 0 && methodsForActiveCategory.length === 0">
|
||||
<div class="empty-selected">–</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div v-if="selectedItems.length === 0" class="empty-selected">–</div>
|
||||
<div
|
||||
v-else
|
||||
v-for="(item, idx) in selectedItems"
|
||||
:key="'project-' + item.id"
|
||||
:key="idx"
|
||||
class="selected-item-card"
|
||||
:class="{ 'is-expanded': item.projectFoldExpanded }"
|
||||
:class="{ 'is-expanded': item.expanded }"
|
||||
>
|
||||
<div
|
||||
class="fold-strip fold-strip-project"
|
||||
:class="{ 'is-open': item.projectFoldExpanded }"
|
||||
>
|
||||
<div class="fold-strip-header" @click="toggleProjectFold(item)">
|
||||
<el-icon :class="['fold-chevron', { open: item.projectFoldExpanded }]">
|
||||
<ArrowDown />
|
||||
</el-icon>
|
||||
<div class="fold-header-main">
|
||||
<span class="fold-kicker">检查项目</span>
|
||||
<el-tooltip :content="getDisplayItemName(item)" placement="top" :show-after="400">
|
||||
<span class="fold-title line-clamp-2">{{ getDisplayItemName(item) }}</span>
|
||||
</el-tooltip>
|
||||
<!-- 项目卡片头部:项目和检查方法解耦,点击展开查看方法/明细 -->
|
||||
<div class="card-header" @click="toggleItemExpand(item)">
|
||||
<el-tooltip :content="getDisplayItemName(item)" placement="top" :show-after="400">
|
||||
<span class="card-name">{{ getDisplayItemName(item) }}</span>
|
||||
</el-tooltip>
|
||||
<span class="card-price">¥{{ formatDetailAmount(getSelectedItemAmount(item)) }}</span>
|
||||
<el-icon :class="['expand-icon', { expanded: item.expanded }]">
|
||||
<ArrowDown />
|
||||
</el-icon>
|
||||
<!-- 删除按钮 -->
|
||||
<el-button link type="danger" size="small" @click.stop="handleRemoveItem(idx, item)">
|
||||
<el-icon><Close /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="item.expanded" class="selected-card-body">
|
||||
<div v-if="shouldShowItemPackageBody(item)">
|
||||
<div class="package-toggle" @click.stop="toggleItemPackageExpand(item)">
|
||||
<span>项目套餐明细</span>
|
||||
<el-icon :class="['expand-icon', { expanded: item.itemPackageExpanded }]">
|
||||
<ArrowDown />
|
||||
</el-icon>
|
||||
</div>
|
||||
<span class="fold-price-strong">¥{{ formatDetailAmount(item.price || 0) }}</span>
|
||||
<el-button link type="danger" size="small" @click.stop="handleRemoveItem(idx, item)">
|
||||
<el-icon><Close /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-show="item.projectFoldExpanded" class="fold-strip-body">
|
||||
<div v-if="shouldShowItemPackageBody(item)" class="fold-package-wrap">
|
||||
<div v-show="item.itemPackageExpanded">
|
||||
<div v-if="item.packageDetailsLoading" class="package-details-loading">加载中...</div>
|
||||
<template v-else>
|
||||
<div v-if="getPackageDetailsList(item).length === 0" class="package-details-empty">
|
||||
暂无套餐明细
|
||||
</div>
|
||||
<div v-else class="package-details-list">
|
||||
<div
|
||||
v-for="(detail, dIdx) in getPackageDetailsList(item)"
|
||||
:key="detail.id ?? detail.itemCode ?? `d-${dIdx}`"
|
||||
class="detail-row"
|
||||
>
|
||||
<el-tooltip :content="detail.name" placement="top" :show-after="500">
|
||||
<span class="detail-name">{{ detail.name }}</span>
|
||||
</el-tooltip>
|
||||
<div class="detail-meta">
|
||||
<span class="detail-qty">×{{ detail.quantity || 1 }}</span>
|
||||
<span class="detail-price">¥{{ formatDetailAmount(detail.price) }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-for="(detail, dIdx) in getPackageDetailsList(item)"
|
||||
:key="detail.id ?? detail.itemCode ?? `d-${dIdx}`"
|
||||
class="detail-row"
|
||||
>
|
||||
<el-tooltip :content="detail.name" placement="top" :show-after="500">
|
||||
<span class="detail-name">{{ detail.name }}</span>
|
||||
</el-tooltip>
|
||||
<div class="detail-meta">
|
||||
<span class="detail-qty">×{{ detail.quantity || 1 }}</span>
|
||||
<span class="detail-price">¥{{ formatDetailAmount(detail.price) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else class="fold-strip-muted">暂无项目套餐明细</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="(method, idx) in selectedMethods"
|
||||
:key="'method-' + method.id"
|
||||
class="selected-item-card"
|
||||
:class="{ 'is-expanded': method.expanded }"
|
||||
>
|
||||
<div
|
||||
class="fold-strip fold-strip-method"
|
||||
:class="{ 'is-open': method.expanded }"
|
||||
>
|
||||
<div class="fold-strip-header" @click="toggleSelectedMethodFold(method)">
|
||||
<el-icon :class="['fold-chevron', { open: method.expanded }]">
|
||||
<ArrowDown />
|
||||
</el-icon>
|
||||
<div class="fold-header-main">
|
||||
<span class="fold-kicker">检查方法</span>
|
||||
<span
|
||||
class="fold-title fold-title-plain line-clamp-2"
|
||||
:title="getDisplayMethodName(method)"
|
||||
>
|
||||
{{ getDisplayMethodName(method) }}
|
||||
</span>
|
||||
<div class="selected-card-section">
|
||||
<div class="selected-section-title">检查方法</div>
|
||||
<div v-if="!item.methods || item.methods.length === 0" class="selected-method-empty">
|
||||
暂无检查方法
|
||||
</div>
|
||||
<span
|
||||
v-if="hasStandaloneMethodPackage(method)"
|
||||
class="fold-price-strong warn"
|
||||
<div
|
||||
v-for="method in item.methods"
|
||||
:key="method.id"
|
||||
class="selected-method-option"
|
||||
>
|
||||
¥{{ formatDetailAmount(method.packagePrice || method.price || 0) }}
|
||||
</span>
|
||||
<el-button link type="danger" size="small" @click.stop="handleRemoveMethod(idx)">
|
||||
<el-icon><Close /></el-icon>
|
||||
</el-button>
|
||||
<el-checkbox
|
||||
:model-value="item.selectedMethod?.id === method.id"
|
||||
@change="(val) => selectMethodCheckbox(val, item, method)"
|
||||
class="method-checkbox"
|
||||
>
|
||||
{{ method.name }}
|
||||
</el-checkbox>
|
||||
<span class="method-price-tag">¥{{ method.packagePrice || method.price || 0 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="method.expanded" class="fold-strip-body">
|
||||
<template v-if="hasStandaloneMethodPackage(method)">
|
||||
<div class="fold-package-wrap fold-method-package-wrap">
|
||||
<div v-if="method.packageLoading" class="package-details-loading">加载中...</div>
|
||||
<template v-else>
|
||||
<div v-if="getStandaloneMethodPackageDetailsList(method).length === 0" class="package-details-empty">
|
||||
暂无检查方法套餐明细
|
||||
<div v-if="shouldShowMethodPackageBody(item)">
|
||||
<div class="package-toggle" @click.stop="toggleMethodPackageExpand(item)">
|
||||
<span>检查方法套餐明细</span>
|
||||
<el-icon :class="['expand-icon', { expanded: item.methodPackageExpanded }]">
|
||||
<ArrowDown />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div v-show="item.methodPackageExpanded">
|
||||
<div v-if="item.methodPackageLoading" class="package-details-loading">加载中...</div>
|
||||
<template v-else>
|
||||
<div v-if="getMethodPackageDetailsList(item).length === 0" class="package-details-empty">
|
||||
暂无检查方法套餐明细
|
||||
</div>
|
||||
<div v-else class="package-details-list method-package-list">
|
||||
<div
|
||||
v-for="(detail, dIdx) in getMethodPackageDetailsList(item)"
|
||||
:key="detail.id ?? detail.itemCode ?? `md-${dIdx}`"
|
||||
class="detail-row"
|
||||
>
|
||||
<el-tooltip :content="detail.name" placement="top" :show-after="500">
|
||||
<span class="detail-name">{{ detail.name }}</span>
|
||||
</el-tooltip>
|
||||
<div class="detail-meta">
|
||||
<span class="detail-qty">×{{ detail.quantity || 1 }}</span>
|
||||
<span class="detail-price">¥{{ formatDetailAmount(detail.price) }}</span>
|
||||
</div>
|
||||
<div v-else class="package-details-list method-package-list">
|
||||
<div
|
||||
v-for="(detail, dIdx) in getStandaloneMethodPackageDetailsList(method)"
|
||||
:key="detail.id ?? detail.itemCode ?? `md-${dIdx}`"
|
||||
class="detail-row"
|
||||
>
|
||||
<el-tooltip :content="detail.name" placement="top" :show-after="500">
|
||||
<span class="detail-name">{{ detail.name }}</span>
|
||||
</el-tooltip>
|
||||
<div class="detail-meta">
|
||||
<span class="detail-qty">×{{ detail.quantity || 1 }}</span>
|
||||
<span class="detail-price">¥{{ formatDetailAmount(detail.price) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="fold-strip-muted">无单独的检查方法套餐明细。</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部:独立勾选检查方法,样式与左侧项目选择一致 -->
|
||||
<div
|
||||
v-if="methodsForActiveCategory.length > 0"
|
||||
class="selected-global-method-picker"
|
||||
@click.stop
|
||||
>
|
||||
<div class="method-picker-collapse-title" @click="methodPickerExpanded = !methodPickerExpanded">
|
||||
<span class="method-picker-title-main">检查方法</span>
|
||||
<span v-if="activeCategoryName" class="global-method-picker-scope">{{ activeCategoryName }}</span>
|
||||
<el-icon :class="['method-picker-arrow', { expanded: methodPickerExpanded }]">
|
||||
<ArrowDown />
|
||||
</el-icon>
|
||||
</div>
|
||||
<div v-show="methodPickerExpanded" class="global-method-picker-list">
|
||||
<div
|
||||
v-for="method in methodsForActiveCategory"
|
||||
:key="'g-m-' + method.id"
|
||||
class="item-row method-picker-row"
|
||||
>
|
||||
<el-checkbox
|
||||
:model-value="isStandaloneMethodSelected(method)"
|
||||
@change="(val) => onStandaloneMethodChange(!!val, method)"
|
||||
class="item-checkbox"
|
||||
>
|
||||
<span class="method-label-inner">{{ formatExamMethodCaption(method.name) }}</span>
|
||||
</el-checkbox>
|
||||
<span class="item-price">¥{{ formatDetailAmount(method.packagePrice || method.price || 0) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -587,8 +539,6 @@ const dictLoading = ref(false);
|
||||
const activeDetailTab = ref('applyForm');
|
||||
const applicationList = ref([]);
|
||||
const selectedItems = ref([]);
|
||||
const selectedMethods = ref([]);
|
||||
const methodPickerExpanded = ref(true);
|
||||
|
||||
// Bug #499: 查询过滤状态
|
||||
const searchForm = reactive({
|
||||
@@ -755,25 +705,13 @@ function getDisplayItemName(item) {
|
||||
return String(item?.name || '').replace(/^套餐[::\-\s]*/, '');
|
||||
}
|
||||
|
||||
/** 检查方法展示:避免与后端文案重复出现「(方法)(方法)」 */
|
||||
function formatExamMethodCaption(name) {
|
||||
const raw = String(name || '').trim();
|
||||
if (!raw) return '';
|
||||
if (/^\(方法\)/.test(raw) || /^(方法)/.test(raw)) {
|
||||
return raw;
|
||||
}
|
||||
return `(方法) ${raw}`;
|
||||
}
|
||||
|
||||
/** 已选方法纯文本(用于标题下级展示,不包含「勾选」前缀,去掉后端自带的 (方法) 前缀) */
|
||||
function getDisplaySelectedMethodName(item) {
|
||||
const raw = String(item?.selectedMethod?.name || '').trim();
|
||||
if (!raw) return '';
|
||||
return raw.replace(/^\(方法\)\s*/, '').replace(/^(方法)\s*/, '').trim();
|
||||
}
|
||||
|
||||
function getSelectedItemAmount(item) {
|
||||
return Number(item?.price || 0);
|
||||
const itemPrice = Number(item?.price || 0);
|
||||
const methodPrice = Number(item?.selectedMethod?.packagePrice || 0);
|
||||
if (!hasMethodPackage(item) || isSamePackage(item)) {
|
||||
return itemPrice;
|
||||
}
|
||||
return itemPrice + methodPrice;
|
||||
}
|
||||
|
||||
function parsePackageDetailsPayload(res) {
|
||||
@@ -905,19 +843,6 @@ const currentActiveCategory = ref(null); // Bug #500: 记录当前激活的分
|
||||
|
||||
const allMethods = ref([]);
|
||||
|
||||
const activeCategory = computed(() => {
|
||||
const id = activeNames.value;
|
||||
if (id === '' || id === null || id === undefined) return null;
|
||||
return categoryList.value.find((cat) => String(cat.typeId) === String(id)) || null;
|
||||
});
|
||||
|
||||
const activeCategoryName = computed(() => activeCategory.value?.typeName || activeCategory.value?.categoryName || '');
|
||||
|
||||
const methodsForActiveCategory = computed(() => {
|
||||
const arr = activeCategory.value?.methods;
|
||||
return Array.isArray(arr) ? arr : [];
|
||||
});
|
||||
|
||||
// ====== 科室下拉(来源:科室管理)======
|
||||
const orgLoading = ref(false);
|
||||
const orgOptions = ref([]); // { label, value }
|
||||
@@ -1028,44 +953,6 @@ const availableMethods = computed(() => {
|
||||
return allMethods.value;
|
||||
});
|
||||
|
||||
function isStandaloneMethodSelected(method) {
|
||||
return selectedMethods.value.some((m) => String(m.id) === String(method?.id));
|
||||
}
|
||||
|
||||
function getDisplayMethodName(method) {
|
||||
const raw = String(method?.name || '').trim();
|
||||
if (!raw) return '';
|
||||
return raw.replace(/^\(方法\)\s*/, '').replace(/^(方法)\s*/, '').trim();
|
||||
}
|
||||
|
||||
function hasStandaloneMethodPackage(method) {
|
||||
return !!(method?.packageId || method?.packageName);
|
||||
}
|
||||
|
||||
function getStandaloneMethodPackageDetailsList(method) {
|
||||
return Array.isArray(method?.packageDetails) ? method.packageDetails : [];
|
||||
}
|
||||
|
||||
async function onStandaloneMethodChange(checked, method) {
|
||||
if (!method) return;
|
||||
if (checked) {
|
||||
if (!isStandaloneMethodSelected(method)) {
|
||||
selectedMethods.value.push({
|
||||
...method,
|
||||
expanded: false,
|
||||
packageLoading: false,
|
||||
packageDetails: []
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const idx = selectedMethods.value.findIndex((m) => String(m.id) === String(method.id));
|
||||
if (idx > -1) selectedMethods.value.splice(idx, 1);
|
||||
}
|
||||
updateMethodDisplay();
|
||||
await nextTick();
|
||||
form.totalAmount = totalAmountCalc.value;
|
||||
}
|
||||
|
||||
// 当可选方法列表改变时,如果当前选中的方法不在新列表中,则清空
|
||||
// #428: 分类展开时联动加载检查方法
|
||||
// Bug #500: 使用 categoryLoadingSet 替代 dictLoading,避免切换分类时整个区域闪烁
|
||||
@@ -1109,8 +996,6 @@ async function handleCategoryExpand(cat) {
|
||||
function handleCollapseChange(activeName) {
|
||||
// 始终记录当前激活的分类,确保 handleCategoryExpand 能正确忽略过期请求
|
||||
currentActiveCategory.value = activeName || null;
|
||||
// 底部「检查方法」勾选区默认展开,不因切换左侧分类而收起
|
||||
methodPickerExpanded.value = true;
|
||||
|
||||
if (activeName) {
|
||||
// Bug #428修复: 直接从 categoryList(原始响应式数组)查找分类,
|
||||
@@ -1120,7 +1005,6 @@ function handleCollapseChange(activeName) {
|
||||
handleCategoryExpand(cat); // 异步加载,不 await
|
||||
}
|
||||
}
|
||||
updateMethodDisplay();
|
||||
}
|
||||
|
||||
watch(availableMethods, (newMethods) => {
|
||||
@@ -1246,26 +1130,17 @@ const filteredCategoryList = computed(() => {
|
||||
|
||||
// ====== 合计 ======
|
||||
const totalAmountCalc = computed(() => {
|
||||
const itemTotal = selectedItems.value.reduce((sum, item) => {
|
||||
const total = selectedItems.value.reduce((sum, item) => {
|
||||
const effectivePrice = getSelectedItemAmount(item);
|
||||
return sum + (effectivePrice * (item.quantity || 1));
|
||||
}, 0);
|
||||
const methodTotal = selectedMethods.value.reduce((sum, method) => {
|
||||
return sum + Number(method?.packagePrice ?? method?.price ?? 0);
|
||||
}, 0);
|
||||
const total = itemTotal + methodTotal;
|
||||
return total.toFixed(2);
|
||||
});
|
||||
|
||||
// 监听已选项:自动更新申检部位
|
||||
watch(selectedItems, () => {
|
||||
form.inspectionArea = selectedItems.value.map(i => i.name).join('+');
|
||||
form.isCharged = selectedItems.value.length > 0 || selectedMethods.value.length > 0 ? 1 : 0;
|
||||
}, { deep: true });
|
||||
|
||||
watch(selectedMethods, () => {
|
||||
form.isCharged = selectedItems.value.length > 0 || selectedMethods.value.length > 0 ? 1 : 0;
|
||||
updateMethodDisplay();
|
||||
form.isCharged = selectedItems.value.length > 0 ? 1 : 0;
|
||||
}, { deep: true });
|
||||
|
||||
// 监听患者变化
|
||||
@@ -1356,7 +1231,6 @@ function handleAdd() {
|
||||
selectedMethodDisplay: '' // Bug #384修复: 重置检查方法显示
|
||||
});
|
||||
selectedItems.value = [];
|
||||
selectedMethods.value = [];
|
||||
resetCategoryChecked();
|
||||
activeDetailTab.value = 'applyForm';
|
||||
// 自动加载临床诊断
|
||||
@@ -1370,27 +1244,22 @@ function handleSave() {
|
||||
ElMessage.warning('请至少选择一个检查明细项目');
|
||||
return;
|
||||
}
|
||||
if (selectedMethods.value.length === 0) {
|
||||
ElMessage.warning('请选择检查方法');
|
||||
// 检查每个项目是否已选择检查方法
|
||||
const itemsWithoutMethod = selectedItems.value.filter(item => !item.selectedMethod);
|
||||
if (itemsWithoutMethod.length > 0) {
|
||||
const names = itemsWithoutMethod.map(item => item.name).join('、');
|
||||
ElMessage.warning(`以下项目未选择检查方法:${names},请在右侧勾选后再保存`);
|
||||
return;
|
||||
}
|
||||
// 从已选项目推导检查类型编码(取第一个项目的 checkType,如 CT / ECG / GI)
|
||||
const firstCheckType = selectedItems.value[0]?.checkType || 'unknown';
|
||||
form.examTypeCode = firstCheckType;
|
||||
form.totalAmount = totalAmountCalc.value;
|
||||
|
||||
|
||||
const primaryMethod = selectedMethods.value[0] || null;
|
||||
const payload = {
|
||||
...form,
|
||||
encounterId: props.patientInfo?.encounterId || null,
|
||||
patientIdNum: props.patientInfo?.patientId || null,
|
||||
checkMethods: selectedMethods.value.map((method) => ({
|
||||
checkMethodId: method.id || null,
|
||||
checkMethodName: method.name || null,
|
||||
checkMethodCode: method.code || null,
|
||||
checkMethodPackageName: method.packageName || null
|
||||
})),
|
||||
items: selectedItems.value.map((item, index) => ({
|
||||
itemCode: String(item.id),
|
||||
itemName: item.name,
|
||||
@@ -1400,10 +1269,10 @@ function handleSave() {
|
||||
itemStatus: 0,
|
||||
itemSeq: index + 1,
|
||||
// 检查方法信息
|
||||
checkMethodId: primaryMethod?.id || null,
|
||||
checkMethodName: primaryMethod?.name || null,
|
||||
checkMethodCode: primaryMethod?.code || null,
|
||||
checkMethodPackageName: primaryMethod?.packageName || null // Bug #384修复: 保存套餐名称
|
||||
checkMethodId: item.selectedMethod?.id || null,
|
||||
checkMethodName: item.selectedMethod?.name || null,
|
||||
checkMethodCode: item.selectedMethod?.code || null,
|
||||
checkMethodPackageName: item.selectedMethod?.packageName || null // Bug #384修复: 保存套餐名称
|
||||
}))
|
||||
};
|
||||
request({
|
||||
@@ -1424,7 +1293,6 @@ function handleRowClick(row) {
|
||||
Object.assign(form, row);
|
||||
form.selectedMethodDisplay = ''; // Bug #384修复: 先清空,后面根据回充数据更新
|
||||
selectedItems.value = [];
|
||||
selectedMethods.value = [];
|
||||
activeDetailTab.value = 'applyForm';
|
||||
request({ url: `/exam/apply/${row.applyNo}`, method: 'get' }).then(async res => {
|
||||
// 响应结构: Axios拦截器对code===200返回res.data(AjaxResult体)
|
||||
@@ -1449,9 +1317,8 @@ function handleRowClick(row) {
|
||||
methods: [],
|
||||
selectedMethod: null,
|
||||
expanded: false,
|
||||
projectFoldExpanded: false,
|
||||
methodFoldExpanded: false,
|
||||
methodPackageExpanded: false,
|
||||
itemPackageExpanded: true,
|
||||
methodPackageExpanded: true,
|
||||
packageDetailsLoading: false,
|
||||
isPackage: false,
|
||||
packageId: null,
|
||||
@@ -1495,21 +1362,7 @@ function handleRowClick(row) {
|
||||
return item;
|
||||
}));
|
||||
// Bug #408修复: 确保明细数据正确加载到selectedItems
|
||||
const methodMap = new Map();
|
||||
for (const item of itemsWithMethods) {
|
||||
if (item.selectedMethod && !methodMap.has(String(item.selectedMethod.id))) {
|
||||
methodMap.set(String(item.selectedMethod.id), {
|
||||
...item.selectedMethod,
|
||||
expanded: false,
|
||||
packageLoading: false,
|
||||
packageDetails: []
|
||||
});
|
||||
}
|
||||
item.selectedMethod = null;
|
||||
item.methodPackageDetails = [];
|
||||
}
|
||||
selectedItems.value = itemsWithMethods;
|
||||
selectedMethods.value = Array.from(methodMap.values());
|
||||
// 加载套餐明细(单个失败不影响其他项目和明细显示)
|
||||
for (const it of selectedItems.value) {
|
||||
if (hasItemPackage(it)) {
|
||||
@@ -1519,17 +1372,14 @@ function handleRowClick(row) {
|
||||
console.error('加载套餐明细失败:', it.name, e);
|
||||
}
|
||||
}
|
||||
it.methodFoldExpanded = false;
|
||||
syncItemExpandedFlag(it);
|
||||
}
|
||||
for (const method of selectedMethods.value) {
|
||||
if (hasStandaloneMethodPackage(method)) {
|
||||
if (hasMethodPackage(it) && !isSamePackage(it)) {
|
||||
try {
|
||||
await loadStandaloneMethodPackageDetails(method);
|
||||
await loadMethodPackageDetails(it, it.selectedMethod);
|
||||
} catch (e) {
|
||||
console.error('加载检查方法套餐明细失败:', method.name, e);
|
||||
console.error('加载检查方法套餐明细失败:', it.name, e);
|
||||
}
|
||||
}
|
||||
it.expanded = shouldShowPackageBody(it);
|
||||
}
|
||||
syncCategoryChecked();
|
||||
// Bug #384修复: 回充后更新检查方法显示
|
||||
@@ -1615,9 +1465,8 @@ async function handleItemSelect(checked, item, cat) {
|
||||
methods: methods,
|
||||
selectedMethod: null,
|
||||
expanded: false,
|
||||
projectFoldExpanded: false,
|
||||
methodFoldExpanded: false,
|
||||
methodPackageExpanded: false,
|
||||
itemPackageExpanded: true,
|
||||
methodPackageExpanded: true,
|
||||
isPackage: !!(item.packageId || item.packageName),
|
||||
packageName: item.packageName || null,
|
||||
packageDetailsLoading: false,
|
||||
@@ -1626,13 +1475,19 @@ async function handleItemSelect(checked, item, cat) {
|
||||
};
|
||||
selectedItems.value.push(newRow);
|
||||
// 必须用数组里的响应式行,不能继续改局部 newRow:push 后列表内是 proxy,改 raw 对象不会触发右侧卡片更新(会一直卡在「加载中」)
|
||||
const row = selectedItems.value[selectedItems.value.length - 1];
|
||||
|
||||
const rowJustAdded = selectedItems.value[selectedItems.value.length - 1];
|
||||
syncItemExpandedFlag(rowJustAdded);
|
||||
|
||||
// 勾选项目只加入项目列表,检查方法由用户在“检查方法”区域手动选择
|
||||
row.selectedMethod = null;
|
||||
updateMethodDisplay();
|
||||
await nextTick();
|
||||
form.totalAmount = totalAmountCalc.value;
|
||||
|
||||
// 新勾选项目后默认展开,直接展示检查方法状态和套餐明细
|
||||
row.expanded = true;
|
||||
row.itemPackageExpanded = true;
|
||||
row.methodPackageExpanded = true;
|
||||
if (hasItemPackage(row)) {
|
||||
await loadPackageDetailsForItem(row);
|
||||
}
|
||||
|
||||
// 自动回填执行科室:按检查项目类型 → 检查类型管理里配置的执行科室
|
||||
if (selectedItems.value.length === 1 && cat?.performDeptName) {
|
||||
@@ -1653,16 +1508,25 @@ async function handleItemSelect(checked, item, cat) {
|
||||
// Bug #382 修复:移除自动切换页签逻辑,保持当前页签状态
|
||||
}
|
||||
|
||||
/** expanded 与各折叠条保持一致(明细表等仍可依赖 expanded) */
|
||||
function syncItemExpandedFlag(row) {
|
||||
if (!row) return;
|
||||
row.expanded = !!(row.projectFoldExpanded || row.methodFoldExpanded);
|
||||
// Bug #384修复 + #426修复: 展开/收起项目卡片
|
||||
async function toggleItemExpand(item) {
|
||||
item.expanded = !item.expanded;
|
||||
if (item.expanded && hasItemPackage(item) && getPackageDetailsList(item).length === 0 && !item.packageDetailsLoading) {
|
||||
await loadPackageDetailsForItem(item);
|
||||
}
|
||||
if (
|
||||
item.expanded &&
|
||||
shouldShowMethodPackageBody(item) &&
|
||||
getMethodPackageDetailsList(item).length === 0 &&
|
||||
!item.methodPackageLoading
|
||||
) {
|
||||
await loadMethodPackageDetails(item, item.selectedMethod);
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleProjectFold(item) {
|
||||
item.projectFoldExpanded = !item.projectFoldExpanded;
|
||||
syncItemExpandedFlag(item);
|
||||
if (item.projectFoldExpanded && hasItemPackage(item) && getPackageDetailsList(item).length === 0 && !item.packageDetailsLoading) {
|
||||
async function toggleItemPackageExpand(item) {
|
||||
item.itemPackageExpanded = !item.itemPackageExpanded;
|
||||
if (item.itemPackageExpanded && getPackageDetailsList(item).length === 0 && !item.packageDetailsLoading) {
|
||||
await loadPackageDetailsForItem(item);
|
||||
}
|
||||
}
|
||||
@@ -1679,64 +1543,25 @@ async function toggleMethodPackageExpand(item) {
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleSelectedMethodFold(method) {
|
||||
method.expanded = !method.expanded;
|
||||
if (
|
||||
method.expanded &&
|
||||
hasStandaloneMethodPackage(method) &&
|
||||
getStandaloneMethodPackageDetailsList(method).length === 0 &&
|
||||
!method.packageLoading
|
||||
) {
|
||||
await loadStandaloneMethodPackageDetails(method);
|
||||
// Bug #384修复: 勾选框选择检查方法(单选逻辑)
|
||||
async function selectMethodCheckbox(checked, item, method) {
|
||||
if (checked) {
|
||||
item.selectedMethod = method;
|
||||
item.expanded = true;
|
||||
item.methodPackageExpanded = true;
|
||||
// 动态加载该方法对应的套餐明细
|
||||
await loadMethodPackageDetails(item, method);
|
||||
} else {
|
||||
item.selectedMethod = null;
|
||||
item.methodPackageDetails = [];
|
||||
}
|
||||
}
|
||||
|
||||
function handleRemoveMethod(idx) {
|
||||
selectedMethods.value.splice(idx, 1);
|
||||
// 联动更新表单检查方法显示字段
|
||||
updateMethodDisplay();
|
||||
}
|
||||
|
||||
async function loadStandaloneMethodPackageDetails(method) {
|
||||
method.packageLoading = true;
|
||||
method.packageDetails = [];
|
||||
try {
|
||||
let packageId = method.packageId;
|
||||
if (!packageId && !method.packageName) {
|
||||
method.packageLoading = false;
|
||||
return;
|
||||
}
|
||||
if (!packageId && method.packageName) {
|
||||
const pkgRes = await listCheckPackage({ packageName: method.packageName });
|
||||
let packages = pkgRes?.data || [];
|
||||
if (!Array.isArray(packages)) {
|
||||
packages = packages.records || packages.data || [];
|
||||
}
|
||||
if (packages.length === 0) {
|
||||
method.packageLoading = false;
|
||||
return;
|
||||
}
|
||||
packageId = packages[0].id;
|
||||
method.packageId = packageId;
|
||||
}
|
||||
const detailRes = await request({
|
||||
url: `/system/check-type/package/${packageId}/details`,
|
||||
method: 'get'
|
||||
});
|
||||
method.packageDetails = parsePackageDetailsPayload(detailRes).map(d => ({
|
||||
id: d.id,
|
||||
name: d.name || d.itemName,
|
||||
quantity: d.quantity || 1,
|
||||
unit: d.unit || '次',
|
||||
price: d.price ?? d.unitPrice ?? d.itemPrice ?? 0,
|
||||
amount: d.amount || d.total || 0,
|
||||
checked: true
|
||||
}));
|
||||
} catch (err) {
|
||||
console.error('加载检查方法套餐明细失败:', err);
|
||||
method.packageDetails = [];
|
||||
} finally {
|
||||
method.packageLoading = false;
|
||||
}
|
||||
// #430: 套餐金额实时同步到申请单
|
||||
nextTick(() => {
|
||||
form.totalAmount = totalAmountCalc.value;
|
||||
});
|
||||
}
|
||||
|
||||
// 根据检查方法的packageName加载对应的套餐明细
|
||||
@@ -1798,12 +1623,9 @@ async function onDetailMethodChange(row, val) {
|
||||
}
|
||||
row.methodPackageDetails = [];
|
||||
updateMethodDisplay();
|
||||
const open = shouldShowPackageBody(row);
|
||||
row.expanded = open;
|
||||
row.projectFoldExpanded = shouldShowItemPackageBody(row) && open;
|
||||
row.methodFoldExpanded = shouldShowMethodPackageBody(row) && open;
|
||||
row.methodPackageExpanded = false;
|
||||
syncItemExpandedFlag(row);
|
||||
row.expanded = shouldShowPackageBody(row);
|
||||
row.itemPackageExpanded = true;
|
||||
row.methodPackageExpanded = true;
|
||||
if (hasItemPackage(row)) {
|
||||
await loadPackageDetailsForItem(row);
|
||||
}
|
||||
@@ -1815,13 +1637,26 @@ async function onDetailMethodChange(row, val) {
|
||||
});
|
||||
}
|
||||
|
||||
// Bug #384修复: 更新检查方法显示字段(取自独立已选检查方法)
|
||||
// Bug #384修复: 更新检查方法显示字段(联动)
|
||||
function updateMethodDisplay() {
|
||||
if (selectedMethods.value.length > 0) {
|
||||
form.selectedMethodDisplay = selectedMethods.value.map((method) => method.name).join('、');
|
||||
return;
|
||||
// 找到第一个有选中检查方法的项目
|
||||
const itemWithMethod = selectedItems.value.find(item => item.selectedMethod);
|
||||
if (itemWithMethod?.selectedMethod) {
|
||||
form.selectedMethodDisplay = itemWithMethod.selectedMethod.name; // 显示检查方法名称,不显示套餐名称
|
||||
} else {
|
||||
form.selectedMethodDisplay = '';
|
||||
}
|
||||
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) {
|
||||
@@ -1835,7 +1670,7 @@ function handleRemoveItem(idx, item) {
|
||||
if (selectedItems.value.length === 0) {
|
||||
form.performDeptCode = '';
|
||||
form.examTypeCode = '';
|
||||
updateMethodDisplay();
|
||||
form.selectedMethodDisplay = ''; // Bug #384修复: 清空检查方法显示
|
||||
} else {
|
||||
// Bug #384修复: 移除后重新计算检查方法显示
|
||||
updateMethodDisplay();
|
||||
@@ -2141,167 +1976,39 @@ defineExpose({ getList });
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 项目上 / 方法下:各自独立下拉条 */
|
||||
.fold-strip {
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.fold-strip:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.fold-strip-header {
|
||||
.selected-item-card .card-header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
padding: 10px 10px;
|
||||
cursor: pointer;
|
||||
gap: 8px;
|
||||
background: linear-gradient(180deg, #f8fafc 0%, #f0f4f8 100%);
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
|
||||
.fold-strip-header:hover {
|
||||
.selected-item-card .card-header:hover {
|
||||
background: linear-gradient(180deg, #ecf5ff 0%, #e3eef8 100%);
|
||||
}
|
||||
|
||||
.fold-strip-method.is-method-target .fold-strip-header {
|
||||
background: linear-gradient(180deg, #e8f3ff 0%, #dceaff 100%);
|
||||
.selected-item-card.is-expanded .card-header {
|
||||
border-bottom-color: #ebeef5;
|
||||
}
|
||||
|
||||
.fold-chevron {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
transition: transform 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.fold-chevron.open {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
.fold-header-main {
|
||||
.card-name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.fold-kicker {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #909399;
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
.fold-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
line-height: 1.35;
|
||||
line-height: 1.4;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.fold-title-plain {
|
||||
font-weight: 500;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.fold-price-strong {
|
||||
.card-price {
|
||||
font-size: 13px;
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.fold-price-strong.warn {
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.fold-strip-body {
|
||||
background: #fafbfc;
|
||||
padding: 0 10px 10px 36px;
|
||||
border-top: 1px dashed var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.fold-package-wrap {
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.fold-strip-muted {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
padding: 10px 0 4px;
|
||||
}
|
||||
|
||||
.selected-global-method-picker {
|
||||
flex-shrink: 0;
|
||||
margin-top: 8px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e4e7ed;
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.method-picker-collapse-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 10px;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.method-picker-collapse-title:hover {
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.method-picker-title-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.global-method-picker-scope {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.method-picker-arrow {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
transition: transform 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.method-picker-arrow.expanded {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
.global-method-picker-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
padding: 6px 8px 8px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.method-picker-row {
|
||||
padding: 6px 4px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
@@ -2351,6 +2058,11 @@ defineExpose({ getList });
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 展开区域 */
|
||||
.selected-card-body {
|
||||
background: #fafbfc;
|
||||
}
|
||||
|
||||
.selected-card-section {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
@@ -2400,49 +2112,6 @@ defineExpose({ getList });
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
/* 收起态:仅展示折叠箭头,不显示「套餐明细」等冗余标题 */
|
||||
.package-toggle-minimal {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 8px 10px;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
cursor: pointer;
|
||||
border-bottom: 1px dashed #e4e7ed;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.package-toggle-minimal:hover {
|
||||
color: #409eff;
|
||||
background: #f5f9ff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.nested-empty-text {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-placeholder);
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
.nested-label-row {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.nested-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-secondary);
|
||||
letter-spacing: 0.03em;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.method-label-inner {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.package-details-loading,
|
||||
.package-details-empty {
|
||||
padding: 12px 10px;
|
||||
|
||||
@@ -179,8 +179,8 @@
|
||||
type="datetime"
|
||||
placeholder="选择执行时间"
|
||||
size="small"
|
||||
format="YYYY-MM-DD HH:mm"
|
||||
value-format="YYYY-MM-DD HH:mm"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
@@ -445,6 +445,7 @@
|
||||
>
|
||||
<el-table-column label="项目名称" prop="itemName" min-width="180">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.isPackage" size="small" type="warning" style="margin-right: 4px">套餐</el-tag>
|
||||
<span :style="{ fontWeight: scope.row.isPackage ? 'bold' : 'normal' }">
|
||||
{{ scope.row.itemName }}
|
||||
</span>
|
||||
@@ -562,6 +563,7 @@
|
||||
@change="toggleInspectionItem(item)"
|
||||
@click.stop
|
||||
/>
|
||||
<el-tag v-if="item.isPackage" size="small" type="warning" style="margin-right: 4px">套餐</el-tag>
|
||||
<span class="item-itemName">{{ item.itemName }}</span>
|
||||
<span class="item-price">¥{{ item.itemPrice }}/{{ item.unit || "次" }}</span>
|
||||
</div>
|
||||
@@ -612,6 +614,7 @@
|
||||
<template v-if="item.isPackage">{{ item.expanded ? '▼' : '▶' }}</template>
|
||||
<template v-else>•</template>
|
||||
</span>
|
||||
<el-tag v-if="item.isPackage" size="small" type="warning" style="margin-right: 4px">套餐</el-tag>
|
||||
<span class="item-itemName">{{ item.itemName }}</span>
|
||||
<span class="item-price">¥{{ item.itemPrice }}/{{ item.unit || "次" }}</span>
|
||||
<el-button
|
||||
@@ -872,30 +875,6 @@ let applyTimeTimer = null
|
||||
const userStore = useUserStore()
|
||||
const { id: userId, name: userName, nickName: userNickName } = storeToRefs(userStore)
|
||||
|
||||
/** 执行时间默认值:当前系统时间,精确到分钟 */
|
||||
const getDefaultExecuteTime = () => {
|
||||
const d = new Date()
|
||||
const year = d.getFullYear()
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
const hours = String(d.getHours()).padStart(2, '0')
|
||||
const minutes = String(d.getMinutes()).padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`
|
||||
}
|
||||
|
||||
/** 将后端时间规范为 YYYY-MM-DD HH:mm */
|
||||
const normalizeExecuteTime = (value) => {
|
||||
if (!value) return getDefaultExecuteTime()
|
||||
const d = new Date(String(value).replace(/-/g, '/'))
|
||||
if (Number.isNaN(d.getTime())) return getDefaultExecuteTime()
|
||||
const year = d.getFullYear()
|
||||
const month = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
const hours = String(d.getHours()).padStart(2, '0')
|
||||
const minutes = String(d.getMinutes()).padStart(2, '0')
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`
|
||||
}
|
||||
|
||||
// 修改 initData 函数
|
||||
const initData = async () => {
|
||||
// 先初始化患者信息(如果有)
|
||||
@@ -904,7 +883,7 @@ const initData = async () => {
|
||||
formData.visitNo = props.patientInfo.busNo || ''
|
||||
formData.patientId = props.patientInfo.patientId || ''
|
||||
formData.patientName = props.patientInfo.patientName || ''
|
||||
formData.medicalrecordNumber = props.patientInfo.identifierNo || ''
|
||||
formData.medicalrecordNumber = props.patientInfo.busNo || ''
|
||||
formData.applyDepartment = props.patientInfo.organizationName || ''
|
||||
formData.applyDocName = userNickName.value || userName.value || ''
|
||||
formData.applyDocCode = userId.value || ''
|
||||
@@ -914,17 +893,13 @@ const initData = async () => {
|
||||
formData.applyOrganizationId = props.patientInfo.orgId || ''
|
||||
formData.encounterId = props.patientInfo.encounterId
|
||||
|
||||
// 执行时间默认填充当前系统时间
|
||||
formData.executeTime = formatDateTime(new Date())
|
||||
|
||||
// 申请单号在保存时由后端生成,此处显示"自动生成"
|
||||
formData.applyNo = '自动生成'
|
||||
// 申请日期实时更新(启动定时器)
|
||||
startApplyTimeTimer()
|
||||
// 执行时间默认当前系统时间(精确到分钟)
|
||||
if (!formData.executeTime) {
|
||||
formData.executeTime = getDefaultExecuteTime()
|
||||
}
|
||||
|
||||
// 执行时间默认填充当前系统时间
|
||||
formData.executeTime = formatDateTime(new Date())
|
||||
|
||||
// 获取主诊断信息
|
||||
try {
|
||||
@@ -1003,7 +978,7 @@ const formData = reactive({
|
||||
applyDeptCode: '',
|
||||
specimenName: '血液',
|
||||
encounterId: '',
|
||||
executeTime: getDefaultExecuteTime(),
|
||||
executeTime: null,
|
||||
applicationType: 0
|
||||
})
|
||||
|
||||
@@ -1551,7 +1526,7 @@ const resetForm = async () => {
|
||||
applicationId: null,
|
||||
applyOrganizationId: props.patientInfo.orgId || '',
|
||||
patientName: props.patientInfo.patientName || '',
|
||||
medicalrecordNumber: props.patientInfo.identifierNo || '',
|
||||
medicalrecordNumber: props.patientInfo.busNo || '',
|
||||
natureofCost: 'self',
|
||||
applyTime: '', // 申请日期由定时器实时更新
|
||||
applyDepartment: props.patientInfo.organizationName || '',
|
||||
@@ -1575,7 +1550,7 @@ const resetForm = async () => {
|
||||
visitNo: '',
|
||||
specimenName: '血液',
|
||||
encounterId: props.patientInfo.encounterId || '',
|
||||
executeTime: getDefaultExecuteTime(),
|
||||
executeTime: formatDateTime(new Date()),
|
||||
applicationType: 0,
|
||||
})
|
||||
selectedInspectionItems.value = []
|
||||
@@ -1625,7 +1600,7 @@ const handleSave = () => {
|
||||
// 修复【#406】:保存前尝试从 props 同步患者信息,避免因加载时序导致信息缺失
|
||||
if ((!formData.patientName?.trim() || !formData.medicalrecordNumber?.trim()) && props.patientInfo && props.patientInfo.encounterId) {
|
||||
formData.patientName = props.patientInfo.patientName || ''
|
||||
formData.medicalrecordNumber = props.patientInfo.identifierNo || ''
|
||||
formData.medicalrecordNumber = props.patientInfo.identifierNo || props.patientInfo.visitNo || ''
|
||||
formData.encounterId = props.patientInfo.encounterId || ''
|
||||
formData.visitNo = props.patientInfo.busNo || ''
|
||||
formData.patientId = props.patientInfo.patientId || ''
|
||||
@@ -2013,7 +1988,7 @@ const loadApplicationToForm = async (row) => {
|
||||
visitNo: detail.visitNo,
|
||||
specimenName: detail.specimenName,
|
||||
encounterId: detail.encounterId,
|
||||
executeTime: normalizeExecuteTime(detail.executeTime),
|
||||
executeTime: detail.executeTime || null,
|
||||
applicationType: detail.applicationType ?? 0
|
||||
})
|
||||
|
||||
@@ -2025,7 +2000,7 @@ const loadApplicationToForm = async (row) => {
|
||||
// Bug #387修复: 套餐项目默认展开,并自动加载明细
|
||||
selectedInspectionItems.value = detail.labApplyItemList.map(item => {
|
||||
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.feePackageId !== '' && item.feePackageId !== 'null' && item.packageName
|
||||
|
||||
return {
|
||||
itemId: itemId,
|
||||
|
||||
@@ -89,14 +89,8 @@ const getList = async () => {
|
||||
const response = await listPendingEmr(queryParams)
|
||||
// 根据后端返回的数据结构调整
|
||||
if (response.code === 200) {
|
||||
const data = response.data
|
||||
if (data && data.rows !== undefined) {
|
||||
emrList.value = data.rows || []
|
||||
total.value = data.total || 0
|
||||
} else {
|
||||
emrList.value = Array.isArray(data) ? data : []
|
||||
total.value = emrList.value.length
|
||||
}
|
||||
emrList.value = response.data || []
|
||||
total.value = Array.isArray(response.data) ? response.data.length : 0
|
||||
} else {
|
||||
ElMessage.error(response.msg || '获取待写病历列表失败')
|
||||
emrList.value = []
|
||||
|
||||
@@ -18,12 +18,16 @@
|
||||
<!-- <el-table-column label="组套类型" align="center" prop="typeEnum_enumText" /> -->
|
||||
<el-table-column label="单次剂量" align="center" prop="rangeCode_dictText">
|
||||
<template #default="scope">
|
||||
{{ formatHistoryDose(scope.row) }}
|
||||
{{
|
||||
scope.row.dose
|
||||
? formatNumber(scope.row.dose) + ' ' + scope.row.doseUnitCode_dictText
|
||||
: ''
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总量" align="center" prop="rangeCode_dictText">
|
||||
<template #default="scope">
|
||||
{{ formatHistoryTotalQuantity(scope.row) }}
|
||||
{{ scope.row.quantity ? scope.row.quantity + ' ' + scope.row.unitCode_dictText : '' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="频次/用法" align="center" prop="rangeCode_dictText" width="200">
|
||||
@@ -86,31 +90,6 @@ const queryParams = ref({
|
||||
typeEnum: 1,
|
||||
});
|
||||
|
||||
function formatHistoryTotalQuantity(row) {
|
||||
if (!row || row.quantity == null || row.quantity === '') return '';
|
||||
const fromDict = row.unitCode_dictText ?? row.minUnitCode_dictText ?? row.unitCodeName;
|
||||
let u =
|
||||
fromDict != null && String(fromDict).trim() !== ''
|
||||
&& String(fromDict).toLowerCase() !== 'null'
|
||||
? String(fromDict).trim()
|
||||
: '';
|
||||
if (!u) {
|
||||
const t = Number(row.adviceType);
|
||||
if (t === 3 || t === 6 || t === 23 || t === 5) u = '次';
|
||||
else if (t === 4) u = '个';
|
||||
}
|
||||
return u ? `${row.quantity} ${u}` : String(row.quantity);
|
||||
}
|
||||
|
||||
function formatHistoryDose(row) {
|
||||
if (!row?.dose) return '';
|
||||
const du = row.doseUnitCode_dictText;
|
||||
if (du != null && String(du).trim() !== '' && String(du).toLowerCase() !== 'null') {
|
||||
return formatNumber(row.dose) + ' ' + du;
|
||||
}
|
||||
return formatNumber(row.dose);
|
||||
}
|
||||
|
||||
function handleOpen() {
|
||||
drawer.value = true;
|
||||
getList();
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
<span>{{ index + 1 + '. ' }}</span>
|
||||
<span>{{ medItem.adviceName }}</span>
|
||||
<span>{{ '(' + medItem.volume + ')' }}</span>
|
||||
<span>{{ formatPrintLineQuantity(medItem) }}</span>
|
||||
<span>{{ medItem.quantity + ' ' + medItem.unitCode_dictText }}</span>
|
||||
<span>{{ '批次号:' + medItem.lotNumber }}</span>
|
||||
<div>
|
||||
<span>用法用量:</span>
|
||||
@@ -161,22 +161,6 @@ const props = defineProps({
|
||||
const emit = defineEmits(['close']);
|
||||
|
||||
//合计
|
||||
function formatPrintLineQuantity(row) {
|
||||
if (row == null || row.quantity == null || row.quantity === '') return '';
|
||||
const fromDict = row.unitCode_dictText ?? row.minUnitCode_dictText ?? row.unitCodeName;
|
||||
let u =
|
||||
fromDict != null && String(fromDict).trim() !== ''
|
||||
&& String(fromDict).toLowerCase() !== 'null'
|
||||
? String(fromDict).trim()
|
||||
: '';
|
||||
if (!u) {
|
||||
const t = Number(row.adviceType);
|
||||
if (t === 3 || t === 6 || t === 23 || t === 5) u = '次';
|
||||
else if (t === 4) u = '个';
|
||||
}
|
||||
return u ? `${row.quantity} ${u}` : String(row.quantity);
|
||||
}
|
||||
|
||||
function getTotalPrice(item) {
|
||||
let totalPrice = new Decimal(0);
|
||||
item.prescriptionInfoDetailList.forEach((medItem) => {
|
||||
|
||||
@@ -686,15 +686,7 @@
|
||||
<span style="margin-left: 4px">{{ scope.row.doseUnitCode_dictText }}</span>
|
||||
</template>
|
||||
<span v-else>
|
||||
{{
|
||||
scope.row.dose
|
||||
? scope.row.dose +
|
||||
(scope.row.doseUnitCode_dictText &&
|
||||
String(scope.row.doseUnitCode_dictText).toLowerCase() !== 'null'
|
||||
? ' ' + scope.row.doseUnitCode_dictText
|
||||
: '')
|
||||
: ''
|
||||
}}
|
||||
{{ scope.row.dose ? scope.row.dose + ' ' + scope.row.doseUnitCode_dictText : '' }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -711,10 +703,10 @@
|
||||
@change="calculateTotalPrice(scope.row, scope.$index)"
|
||||
@input="calculateTotalPrice(scope.row, scope.$index)"
|
||||
/>
|
||||
<span style="margin-left: 4px">{{ resolveTotalQuantityUnit(scope.row) }}</span>
|
||||
<span style="margin-left: 4px">{{ scope.row.unitCode_dictText }}</span>
|
||||
</template>
|
||||
<span v-else>
|
||||
{{ formatTotalQuantityWithUnit(scope.row) }}
|
||||
{{ scope.row.quantity ? scope.row.quantity + ' ' + scope.row.unitCode_dictText : '' }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -925,39 +917,6 @@ const unitMap = ref({
|
||||
minUnit: 'minUnit',
|
||||
unit: 'unit',
|
||||
});
|
||||
|
||||
/** 解析总量单位文案(无字典时:诊疗/手术/检查默认「次」,耗材默认「个」) */
|
||||
const resolveTotalQuantityUnit = (row) => {
|
||||
if (row == null) return '';
|
||||
const fromDict =
|
||||
row.unitCode_dictText ?? row.minUnitCode_dictText ?? row.unitCodeName;
|
||||
let unitStr =
|
||||
fromDict != null && String(fromDict).trim() !== ''
|
||||
&& String(fromDict).toLowerCase() !== 'null'
|
||||
? String(fromDict).trim()
|
||||
: '';
|
||||
if (!unitStr) {
|
||||
const t = Number(row.adviceType);
|
||||
// drord_doctor_type: 3=诊疗 4=耗材 5=会诊 6=手术;23=检查(特殊)
|
||||
// 注意:2=中成药(药品),不可用「次」作为默认单位
|
||||
if (t === 3 || t === 6 || t === 23 || t === 5) {
|
||||
unitStr = '次';
|
||||
} else if (t === 4) {
|
||||
unitStr = '个';
|
||||
}
|
||||
}
|
||||
return unitStr;
|
||||
};
|
||||
|
||||
/** 总量列展示:避免 unitCode_dictText 为空时显示「1 null」 */
|
||||
const formatTotalQuantityWithUnit = (row) => {
|
||||
if (row == null) return '';
|
||||
const q = row.quantity;
|
||||
if (q === undefined || q === null || q === '') return '';
|
||||
const unitStr = resolveTotalQuantityUnit(row);
|
||||
return unitStr ? `${q} ${unitStr}` : String(q);
|
||||
};
|
||||
|
||||
const buttonDisabled = computed(() => {
|
||||
return !props.patientInfo;
|
||||
});
|
||||
@@ -2755,8 +2714,7 @@ function handleEmrTreatment() {
|
||||
treatment += '诊疗[' + (index + 1) + ']' + ' ';
|
||||
treatment += item.adviceName + ' ';
|
||||
if (item.quantity) {
|
||||
const u = resolveTotalQuantityUnit(item);
|
||||
treatment += '数量:' + item.quantity + (u ? ' ' + u : '') + ' ';
|
||||
treatment += '数量:' + item.quantity + item.unitCode_dictText + ' ';
|
||||
}
|
||||
treatment += '频次:' + item.rateCode_dictText + ' ';
|
||||
if (item.methodCode_dictText) {
|
||||
|
||||
@@ -113,17 +113,10 @@ const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const response = await listPendingEmr(queryParams)
|
||||
// 根据后端返回的数据结构调整
|
||||
if (response.code === 200) {
|
||||
const data = response.data
|
||||
if (data && data.rows !== undefined) {
|
||||
// 新分页格式 {rows, total}
|
||||
emrList.value = data.rows || []
|
||||
total.value = data.total || 0
|
||||
} else {
|
||||
// 兼容旧格式(数组)
|
||||
emrList.value = Array.isArray(data) ? data : []
|
||||
total.value = emrList.value.length
|
||||
}
|
||||
emrList.value = response.data || []
|
||||
total.value = Array.isArray(response.data) ? response.data.length : 0
|
||||
} else {
|
||||
ElMessage.error(response.msg || '获取待写病历列表失败')
|
||||
emrList.value = []
|
||||
|
||||
@@ -58,11 +58,7 @@
|
||||
<el-table-column prop="busNo" label="单据号" align="center" width="150" />
|
||||
<el-table-column prop="applicantName" label="申请人" align="center" width="100" />
|
||||
<el-table-column prop="locationName" label="发药药房" align="center" />
|
||||
<el-table-column prop="statusEnum_enumText" label="状态" align="center">
|
||||
<template #default="scope">
|
||||
{{ formatSummaryStatusText(scope.row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="statusEnum_enumText" label="状态" align="center" />
|
||||
<el-table-column prop="applyTime" label="汇总日期" align="center" width="140">
|
||||
<template #default="scope">
|
||||
{{ scope.row.applyTime ? parseTime(scope.row.applyTime, '{y}-{m}-{d}') : '-' }}
|
||||
@@ -143,32 +139,6 @@ import {getCurrentInstance, ref} from 'vue';
|
||||
import {getFromSummaryDetails, getFromSummaryInit, getFromSummaryList, totalSendDrug,} from './api.js';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
/** 发药汇总单状态展示(汇总申请→已提交,发药→已发药) */
|
||||
const SUMMARY_STATUS_DISPLAY = {
|
||||
2: '已提交',
|
||||
4: '已发药',
|
||||
};
|
||||
const LEGACY_SUMMARY_STATUS_TEXT = {
|
||||
待配药: '已提交',
|
||||
已发放: '已发药',
|
||||
};
|
||||
|
||||
function formatSummaryStatusText(row) {
|
||||
const code = Number(row?.statusEnum);
|
||||
if (SUMMARY_STATUS_DISPLAY[code]) {
|
||||
return SUMMARY_STATUS_DISPLAY[code];
|
||||
}
|
||||
return LEGACY_SUMMARY_STATUS_TEXT[row?.statusEnum_enumText] || row?.statusEnum_enumText || '-';
|
||||
}
|
||||
|
||||
function mapSummaryStatusOptions(options = []) {
|
||||
return options.map((item) => ({
|
||||
...item,
|
||||
label: SUMMARY_STATUS_DISPLAY[item.value] ?? LEGACY_SUMMARY_STATUS_TEXT[item.label] ?? item.label,
|
||||
}));
|
||||
}
|
||||
|
||||
const statusEnumOptions = ref([]);
|
||||
const summaryList = ref([]);
|
||||
const queryParams = ref({
|
||||
@@ -252,7 +222,7 @@ function handleSend(row) {
|
||||
const getStatusOption = async () => {
|
||||
try {
|
||||
const res = await getFromSummaryInit();
|
||||
statusEnumOptions.value = mapSummaryStatusOptions(res.data.dispenseStatusOptions);
|
||||
statusEnumOptions.value = res.data.dispenseStatusOptions;
|
||||
} catch (error) {}
|
||||
};
|
||||
getStatusOption();
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {computed, nextTick, ref} from 'vue';
|
||||
import {computed, nextTick, onMounted, ref} from 'vue';
|
||||
import {throttle} from 'lodash-es';
|
||||
import Table from '@/components/TableLayout/Table.vue';
|
||||
import {getAdviceBaseInfo} from './api';
|
||||
@@ -204,6 +204,11 @@ defineExpose({
|
||||
handleKeyDown,
|
||||
refresh,
|
||||
});
|
||||
|
||||
// 组件挂载时自动加载数据(el-popover 懒渲染,父组件 refresh 可能因时序问题未生效,onMounted 最可靠)
|
||||
onMounted(() => {
|
||||
getList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -250,11 +250,10 @@ export function getContract(params) {
|
||||
/**
|
||||
* 获取科室列表
|
||||
*/
|
||||
export function getOrgTree(params = {}) {
|
||||
export function getOrgTree() {
|
||||
return request({
|
||||
url: '/base-data-manage/organization/organization',
|
||||
method: 'get',
|
||||
params: { pageNo: 1, pageSize: 5000, ...params },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -115,18 +115,20 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="requesterId_dictText" label="申请者" width="120" />
|
||||
<el-table-column label="操作" align="center" fixed="right" width="280">
|
||||
<el-table-column label="操作" align="center" fixed="right" width="220">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
|
||||
<template v-if="canManageRow(scope.row) && isPendingStatus(scope.row)">
|
||||
<!-- 待签发:可修改、删除 -->
|
||||
<template v-if="isPendingStatus(scope.row)">
|
||||
<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-if="canManageRow(scope.row) && isWithdrawableStatus(scope.row)">
|
||||
<!-- 已签发:可撤回 -->
|
||||
<template v-else-if="isIssuedStatus(scope.row)">
|
||||
<el-button link type="warning" @click="handleWithdraw(scope.row)">撤回</el-button>
|
||||
</template>
|
||||
<template v-if="isReportStatus(scope.row)">
|
||||
<el-button link type="success" @click="handleViewReport(scope.row)">查看报告</el-button>
|
||||
<!-- 已采证、已送检、报告已出、已作废:仅查看详情 -->
|
||||
<template v-else>
|
||||
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -226,10 +228,7 @@ import {patientInfo} from '../../store/patient.js';
|
||||
import {getInspection, deleteRequestForm, withdrawRequestForm, getProofResult} from './api';
|
||||
import {getDepartmentList} from '@/api/public.js';
|
||||
import LaboratoryTests from '../order/applicationForm/laboratoryTests.vue';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import auth from '@/plugins/auth';
|
||||
|
||||
const userStore = useUserStore();
|
||||
import {saveInspection} from '../order/applicationForm/api.js';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
@@ -379,25 +378,10 @@ const isPendingStatus = (row) => {
|
||||
return status === undefined || status === null || status === '' || String(status) === '0';
|
||||
};
|
||||
|
||||
const isWithdrawableStatus = (row) => String(getBillStatus(row)) === '1';
|
||||
const isIssuedStatus = (row) => String(getBillStatus(row)) === '1';
|
||||
|
||||
const isReportStatus = (row) => ['6', '8'].includes(String(getBillStatus(row)));
|
||||
|
||||
/**
|
||||
* 是否可管理该申请单:申请者本人或管理员
|
||||
*/
|
||||
const canManageRow = (row) => {
|
||||
if (auth.hasRole('admin')) {
|
||||
return true;
|
||||
}
|
||||
const currentPractitionerId = userStore.practitionerId;
|
||||
const requesterId = row?.requesterId;
|
||||
if (!currentPractitionerId || !requesterId) {
|
||||
return false;
|
||||
}
|
||||
return String(currentPractitionerId) === String(requesterId);
|
||||
};
|
||||
|
||||
const sortByCreateTimeDesc = (a, b) => {
|
||||
const aTime = a?.createTime ? new Date(a.createTime).getTime() : 0;
|
||||
const bTime = b?.createTime ? new Date(b.createTime).getTime() : 0;
|
||||
@@ -602,9 +586,9 @@ const submitEditForm = () => {
|
||||
*/
|
||||
const handleDelete = async (row) => {
|
||||
try {
|
||||
await proxy.$modal?.confirm?.('确认作废该申请单吗?作废后不可撤销');
|
||||
await proxy.$modal?.confirm?.(`确定要删除申请单 "${row.prescriptionNo}" 吗?此操作不可恢复。`);
|
||||
} catch {
|
||||
return;
|
||||
return; // 用户取消
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -615,21 +599,19 @@ const handleDelete = async (row) => {
|
||||
} else {
|
||||
proxy.$modal?.msgError?.(res?.msg || '删除失败');
|
||||
}
|
||||
} catch {
|
||||
// 响应拦截器已处理错误提示,此处静默
|
||||
} catch (e) {
|
||||
proxy.$modal?.msgError?.(e.message || '删除异常');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 撤回检验申请单(已签发且未采证状态可撤回)
|
||||
* 撤回检验申请单(已签发状态撤回至待签发)
|
||||
*/
|
||||
const handleWithdraw = async (row) => {
|
||||
try {
|
||||
await proxy.$modal?.confirm?.(
|
||||
'确认撤回该申请单吗?撤回后申请单及关联医嘱将恢复为待签发状态,护士站将同步更新。'
|
||||
);
|
||||
await proxy.$modal?.confirm?.(`确定要撤回申请单 "${row.prescriptionNo}" 吗?撤回后将恢复为待签发状态。`);
|
||||
} catch {
|
||||
return;
|
||||
return; // 用户取消
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -640,8 +622,8 @@ const handleWithdraw = async (row) => {
|
||||
} else {
|
||||
proxy.$modal?.msgError?.(res?.msg || '撤回失败');
|
||||
}
|
||||
} catch {
|
||||
// 响应拦截器已处理错误提示,此处静默
|
||||
} catch (e) {
|
||||
proxy.$modal?.msgError?.(e.message || '撤回异常');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -758,10 +740,6 @@ defineExpose({
|
||||
|
||||
.report-status-tag {
|
||||
cursor: pointer;
|
||||
background-color: #f0f9eb !important;
|
||||
border-color: #67c23a !important;
|
||||
color: #529b2e !important;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@keyframes rotating {
|
||||
|
||||
@@ -633,11 +633,11 @@ const calculateTotalAmount = () => {
|
||||
nextTick(() => {
|
||||
const row = props.row;
|
||||
const qty = new Decimal(row.doseQuantity || 0);
|
||||
// 根据首次用量单位类型决定使用哪个单价
|
||||
const unitType = row.unitCodeList?.find((k) => k.value == row.doseUnitCode)?.type;
|
||||
const price = unitType == 'unit' ? row.unitPrice : row.minUnitPrice;
|
||||
const isMinUnit = row.unitCode == row.minUnitCode;
|
||||
const price = isMinUnit ? row.minUnitPrice : row.unitPrice;
|
||||
// 四舍五入到2位再算,与页面显示的单价一致
|
||||
const roundedPrice = new Decimal(price || 0).toDecimalPlaces(2, Decimal.ROUND_HALF_UP);
|
||||
row.totalPrice = qty.mul(roundedPrice).toDecimalPlaces(2, Decimal.ROUND_HALF_UP).toString();
|
||||
row.totalPrice = qty.mul(roundedPrice).toFixed(6);
|
||||
});
|
||||
};
|
||||
const setInputRef = props.handlers.setInputRef;
|
||||
|
||||
@@ -24,20 +24,22 @@
|
||||
</el-col> -->
|
||||
<el-col :span="12">
|
||||
<el-form-item label="发往科室" prop="targetDepartment" style="width: 100%">
|
||||
<el-select
|
||||
<!-- <el-input v-model="form.targetDepartment" autocomplete="off" /> -->
|
||||
<el-tree-select
|
||||
clearable
|
||||
style="width: 100%"
|
||||
v-model="form.targetDepartment"
|
||||
filterable
|
||||
clearable
|
||||
:data="orgOptions"
|
||||
:props="{
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
children: 'children',
|
||||
}"
|
||||
value-key="id"
|
||||
check-strictly
|
||||
placeholder="请选择科室"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in flatOrgOptions"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
@@ -76,33 +78,18 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup name="BloodTransfusion">
|
||||
import {computed, getCurrentInstance, nextTick, onBeforeMount, onMounted, reactive, ref, watch} from 'vue';
|
||||
import {ElMessage} from 'element-plus';
|
||||
import {getCurrentInstance, onBeforeMount, onMounted, reactive, ref} from 'vue';
|
||||
import {patientInfo} from '../../../store/patient.js';
|
||||
import {getDepartmentList} from '@/api/public.js';
|
||||
import request from '@/utils/request';
|
||||
import {getDiagnosisTreatmentOne} from '@/views/catalog/diagnosistreatment/components/diagnosistreatment';
|
||||
import {getEncounterDiagnosis} from '../../api.js';
|
||||
import {getApplicationList, saveBloodTransfusio} from './api';
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
/** 科室树节点 id 统一为字符串,避免大整数精度丢失导致 tree-select 无法匹配 */
|
||||
const normalizeOrgTreeIds = (nodes) => {
|
||||
if (!Array.isArray(nodes)) return [];
|
||||
return nodes.map((node) => ({
|
||||
...node,
|
||||
id: node.id != null ? String(node.id) : node.id,
|
||||
children: node.children?.length ? normalizeOrgTreeIds(node.children) : undefined,
|
||||
}));
|
||||
};
|
||||
|
||||
// 递归查找树形科室节点
|
||||
const findTreeItem = (list, id) => {
|
||||
if (!list || list.length === 0 || id == null || id === '') return null;
|
||||
const strId = String(id);
|
||||
if (!list || list.length === 0) return null;
|
||||
for (const item of list) {
|
||||
if (String(item.id) === strId) return item;
|
||||
if (item.id == id) return item;
|
||||
if (item.children && item.children.length > 0) {
|
||||
const found = findTreeItem(item.children, id);
|
||||
if (found) return found;
|
||||
@@ -110,149 +97,11 @@ const findTreeItem = (list, id) => {
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/** 在科室树中解析 orgId(兼容 Long 转 Number 后的精度丢失) */
|
||||
const resolveOrgIdInTree = (rawOrgId) => {
|
||||
if (rawOrgId == null || rawOrgId === '') return '';
|
||||
const strOrgId = String(rawOrgId);
|
||||
const findInTree = (nodes) => {
|
||||
if (!nodes?.length) return null;
|
||||
for (const node of nodes) {
|
||||
if (String(node.id) === strOrgId) return String(node.id);
|
||||
if (
|
||||
typeof node.id === 'string' &&
|
||||
node.id.length >= 16 &&
|
||||
strOrgId.length >= 16 &&
|
||||
node.id.substring(0, 15) === strOrgId.substring(0, 15)
|
||||
) {
|
||||
return String(node.id);
|
||||
}
|
||||
if (node.children?.length) {
|
||||
const found = findInTree(node.children);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
return findInTree(orgOptions.value) || strOrgId;
|
||||
};
|
||||
|
||||
const resolveTargetDepartmentId = (rawId) => {
|
||||
if (rawId == null || rawId === '') return '';
|
||||
const resolved = resolveOrgIdInTree(rawId);
|
||||
const node = findTreeItem(orgOptions.value, resolved);
|
||||
return node ? String(node.id) : resolved;
|
||||
};
|
||||
|
||||
/** 诊疗目录「所属科室」→ AdviceBaseDto.orgId */
|
||||
const getBelongingOrgId = (item) => {
|
||||
if (!item) return null;
|
||||
return item.orgId ?? item.org_id ?? null;
|
||||
};
|
||||
|
||||
/** 诊疗目录所属科室名称(字典翻译字段) */
|
||||
const getBelongingOrgName = (item) => {
|
||||
if (!item) return '';
|
||||
return item.orgId_dictText || item.orgName || item.org_name || '';
|
||||
};
|
||||
|
||||
/** 按机构 ID 拉取科室名称(树中无节点时兜底) */
|
||||
const fetchOrgNameById = async (orgId) => {
|
||||
if (orgId == null || orgId === '') return '';
|
||||
const fromTree = findOrgName(orgId);
|
||||
if (fromTree) return fromTree;
|
||||
try {
|
||||
const res = await request({
|
||||
url: '/base-data-manage/organization/organization-getById',
|
||||
method: 'get',
|
||||
params: { orgId },
|
||||
});
|
||||
if (res.code === 200 && res.data?.name) {
|
||||
return res.data.name;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('查询科室名称失败', e);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
/** 从机构树解析科室名称 */
|
||||
const findOrgName = (orgId) => {
|
||||
if (orgId == null || orgId === '') return '';
|
||||
const node = findTreeItem(orgOptions.value, orgId);
|
||||
if (node?.name) return node.name;
|
||||
const resolved = resolveOrgIdInTree(orgId);
|
||||
const resolvedNode = findTreeItem(orgOptions.value, resolved);
|
||||
return resolvedNode?.name || '';
|
||||
};
|
||||
|
||||
/** 自动填充时缓存的科室名称 */
|
||||
const targetDepartmentName = ref('');
|
||||
|
||||
/** 扁平化科室选项,保证 el-select 能稳定显示 label */
|
||||
const flatOrgOptions = computed(() => {
|
||||
const options = [];
|
||||
const seen = new Set();
|
||||
const walk = (nodes) => {
|
||||
for (const node of nodes || []) {
|
||||
if (node?.id == null) continue;
|
||||
const value = String(node.id);
|
||||
if (seen.has(value)) continue;
|
||||
seen.add(value);
|
||||
options.push({ value, label: node.name || value });
|
||||
if (node.children?.length) walk(node.children);
|
||||
}
|
||||
};
|
||||
walk(orgOptions.value);
|
||||
const curId = form.targetDepartment;
|
||||
const curName = targetDepartmentName.value || findOrgName(curId);
|
||||
if (curId && curName && !seen.has(String(curId))) {
|
||||
options.unshift({ value: String(curId), label: curName });
|
||||
}
|
||||
return options;
|
||||
});
|
||||
|
||||
/** 从诊疗目录详情补全所属科室(医嘱下拉接口不带 orgId_dictText) */
|
||||
const resolveProjectOrgInfo = async (item) => {
|
||||
if (!item) return { orgId: null, orgName: '' };
|
||||
let orgId = getBelongingOrgId(item);
|
||||
let orgName = getBelongingOrgName(item);
|
||||
if ((!orgId || !orgName) && item.adviceDefinitionId) {
|
||||
try {
|
||||
const res = await getDiagnosisTreatmentOne(item.adviceDefinitionId);
|
||||
const detail = res?.data;
|
||||
if (detail) {
|
||||
orgId = orgId ?? detail.orgId ?? detail.org_id ?? null;
|
||||
orgName = orgName || detail.orgId_dictText || detail.orgName || '';
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('查询诊疗目录所属科室失败', e);
|
||||
}
|
||||
}
|
||||
if (orgId && !orgName) {
|
||||
orgName = await fetchOrgNameById(orgId);
|
||||
}
|
||||
return { orgId, orgName };
|
||||
};
|
||||
|
||||
/** 写入发往科室 */
|
||||
const applyTargetDepartment = async (belongOrgId, nameHint = '') => {
|
||||
if (belongOrgId == null || belongOrgId === '') {
|
||||
form.targetDepartment = '';
|
||||
targetDepartmentName.value = '';
|
||||
return;
|
||||
}
|
||||
const resolvedDeptId = resolveTargetDepartmentId(belongOrgId);
|
||||
const deptName =
|
||||
nameHint || findOrgName(belongOrgId) || findOrgName(resolvedDeptId) || (await fetchOrgNameById(belongOrgId));
|
||||
targetDepartmentName.value = deptName;
|
||||
form.targetDepartment = resolvedDeptId;
|
||||
};
|
||||
const emits = defineEmits(['submitOk']);
|
||||
const props = defineProps({});
|
||||
const state = reactive({});
|
||||
const applicationListAll = ref([]);
|
||||
const applicationList = ref([]);
|
||||
const applicationListAll = ref();
|
||||
const applicationList = ref();
|
||||
const loading = ref(false);
|
||||
const orgOptions = ref([]); // 科室选项
|
||||
const getList = () => {
|
||||
@@ -269,48 +118,29 @@ const getList = () => {
|
||||
adviceTypes: [3], //1 药品 2耗材 3诊疗
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200 && Array.isArray(res.data?.records)) {
|
||||
const records = res.data.records.filter((item) => item.adviceDefinitionId != null);
|
||||
applicationListAll.value = records;
|
||||
applicationList.value = records.map((item) => {
|
||||
if (res.code === 200) {
|
||||
applicationListAll.value = res.data.records;
|
||||
applicationList.value = res.data.records.map((item) => {
|
||||
const priceInfo = item.priceList?.[0] || {};
|
||||
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
|
||||
const unit = item.unitCode_dictText || item.unitCode || '';
|
||||
const id = item.adviceDefinitionId;
|
||||
return {
|
||||
adviceDefinitionId: id,
|
||||
adviceDefinitionId: item.adviceDefinitionId,
|
||||
orgId: item.orgId,
|
||||
label: item.adviceName + ' (¥' + price + '/' + unit + ')',
|
||||
key: id,
|
||||
key: item.adviceDefinitionId,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
proxy.$message.error(res.message || '加载输血项目失败');
|
||||
applicationListAll.value = [];
|
||||
proxy.$message.error(res.message);
|
||||
applicationList.value = [];
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
if (transferValue.value.length > 0) {
|
||||
nextTick(async () => {
|
||||
const valid = await validateTransferOrgConsistency(transferValue.value);
|
||||
if (valid) {
|
||||
lastValidTransferValue.value = [...transferValue.value];
|
||||
fillTargetDepartmentFromSelection(transferValue.value, 1);
|
||||
} else {
|
||||
transferValue.value = [];
|
||||
lastValidTransferValue.value = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
const transferValue = ref([]);
|
||||
/** 上一次通过校验的已选项目(科室不一致时回滚到此状态) */
|
||||
const lastValidTransferValue = ref([]);
|
||||
const isRevertingTransfer = ref(false);
|
||||
let transferValidateSeq = 0;
|
||||
const form = reactive({
|
||||
// categoryType: '', // 项目类别
|
||||
targetDepartment: '', // 发往科室
|
||||
@@ -327,140 +157,86 @@ const rules = reactive({});
|
||||
onBeforeMount(() => {});
|
||||
onMounted(() => {
|
||||
getList();
|
||||
getLocationInfo();
|
||||
});
|
||||
const collectSelectedProjects = (selectProjectIds) => {
|
||||
return (selectProjectIds || [])
|
||||
.map((element) =>
|
||||
applicationListAll.value.find((item) => String(item.adviceDefinitionId) === String(element))
|
||||
)
|
||||
.filter(Boolean);
|
||||
};
|
||||
|
||||
/** 校验已选项目的所属科室是否一致(超过 1 项时才校验) */
|
||||
const validateTransferOrgConsistency = async (selectProjectIds) => {
|
||||
const arr = collectSelectedProjects(selectProjectIds);
|
||||
if (arr.length <= 1) {
|
||||
return true;
|
||||
}
|
||||
const orgInfoList = await Promise.all(arr.map((item) => resolveProjectOrgInfo(item)));
|
||||
const firstOrgId = orgInfoList[0]?.orgId;
|
||||
return orgInfoList.every((info) => String(info?.orgId ?? '') === String(firstOrgId ?? ''));
|
||||
};
|
||||
|
||||
/**
|
||||
* type(1:watch监听类型 2:点击保存类型)
|
||||
*/
|
||||
const fillTargetDepartmentFromSelection = async (selectProjectIds, type) => {
|
||||
const manualDept = type === 2 && form.targetDepartment ? form.targetDepartment : '';
|
||||
const arr = collectSelectedProjects(selectProjectIds);
|
||||
|
||||
if (arr.length === 0) {
|
||||
// 项目列表尚未加载完时,已选 ID 存在则先不清空(避免误清发往科室)
|
||||
if ((selectProjectIds || []).length > 0 && applicationListAll.value.length === 0) {
|
||||
return type === 2 ? !!manualDept : true;
|
||||
* selectProjectIds(选中项目的id数组)
|
||||
* */
|
||||
const projectWithDepartment = (selectProjectIds, type) => {
|
||||
//1.获取选中的项目 2.判断项目的执行科室是否相同 3.判断执行科室是否配置 4.将项目的执行科室复值到执行科室下拉选位置
|
||||
let isRelease = true;
|
||||
// 选中项目的数组
|
||||
const arr = [];
|
||||
// 根据选中的项目id查找对应的项目
|
||||
selectProjectIds.forEach((element) => {
|
||||
const searchData = applicationList.value.find((item) => {
|
||||
return element == item.adviceDefinitionId;
|
||||
});
|
||||
arr.push(searchData);
|
||||
});
|
||||
// 清空科室
|
||||
form.targetDepartment = '';
|
||||
if (arr.length > 0) {
|
||||
const obj = arr[0];
|
||||
// 判断科室是否相同
|
||||
const isCompare = arr.every((item) => {
|
||||
return item.orgId == obj.orgId;
|
||||
});
|
||||
if (!isCompare) {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: '执行科室不同',
|
||||
});
|
||||
isRelease = false;
|
||||
}
|
||||
form.targetDepartment = '';
|
||||
targetDepartmentName.value = '';
|
||||
return type === 2 ? !!manualDept : true;
|
||||
}
|
||||
// 选中项目中的执行科室id与全部科室数据做匹配
|
||||
const findItem = findTreeItem(orgOptions.value, obj.orgId);
|
||||
|
||||
const orgInfoList = await Promise.all(arr.map((item) => resolveProjectOrgInfo(item)));
|
||||
const firstOrg = orgInfoList[0];
|
||||
const belongOrgId = firstOrg?.orgId;
|
||||
const allSameOrg = orgInfoList.every((info) => String(info?.orgId ?? '') === String(belongOrgId ?? ''));
|
||||
if (!allSameOrg) {
|
||||
if (type === 2) {
|
||||
ElMessage.error('所选项目的所属科室不一致,请分开申请');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (belongOrgId == null || belongOrgId === '') {
|
||||
if (type === 2 && manualDept) {
|
||||
await applyTargetDepartment(manualDept, findOrgName(manualDept));
|
||||
return true;
|
||||
}
|
||||
if (type === 2) {
|
||||
ElMessage.warning('所选项目未在诊疗目录配置所属科室,请手动选择发往科室');
|
||||
return false;
|
||||
}
|
||||
form.targetDepartment = '';
|
||||
targetDepartmentName.value = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type === 2 && manualDept) {
|
||||
await applyTargetDepartment(manualDept, findOrgName(manualDept));
|
||||
return true;
|
||||
}
|
||||
|
||||
await applyTargetDepartment(belongOrgId, firstOrg?.orgName || '');
|
||||
return true;
|
||||
};
|
||||
|
||||
// 选中项目:先校验所属科室一致,不通过则回滚穿梭框,不允许进入「已选择」
|
||||
watch(
|
||||
() => transferValue.value,
|
||||
async (newValue) => {
|
||||
if (isRevertingTransfer.value) return;
|
||||
|
||||
const seq = ++transferValidateSeq;
|
||||
const valid = await validateTransferOrgConsistency(newValue);
|
||||
if (seq !== transferValidateSeq) return;
|
||||
|
||||
if (!valid) {
|
||||
ElMessage.error('所选项目的所属科室不一致,请分开申请');
|
||||
isRevertingTransfer.value = true;
|
||||
transferValue.value = [...lastValidTransferValue.value];
|
||||
await nextTick();
|
||||
isRevertingTransfer.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
lastValidTransferValue.value = [...newValue];
|
||||
await fillTargetDepartmentFromSelection(newValue, 1);
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => orgOptions.value,
|
||||
() => {
|
||||
if (transferValue.value.length > 0) {
|
||||
nextTick(() => {
|
||||
fillTargetDepartmentFromSelection(transferValue.value, 1);
|
||||
if (!findItem) {
|
||||
isRelease = false;
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: '未找到项目执行的科室',
|
||||
});
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
if (type == 1) {
|
||||
if (isRelease) {
|
||||
form.targetDepartment = findItem.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isRelease;
|
||||
};
|
||||
// 监听选择项目变化
|
||||
watch(
|
||||
() => transferValue.value,
|
||||
(newValue) => {
|
||||
projectWithDepartment(newValue, 1);
|
||||
}
|
||||
);
|
||||
const submit = async () => {
|
||||
const submit = () => {
|
||||
if (transferValue.value.length == 0) {
|
||||
return proxy.$message.error('请选择申请单');
|
||||
}
|
||||
if (!(await fillTargetDepartmentFromSelection(transferValue.value, 2))) {
|
||||
if (!projectWithDepartment(transferValue.value, 2)) {
|
||||
return;
|
||||
}
|
||||
if (!form.targetDepartment) {
|
||||
return proxy.$message.error('请选择发往科室');
|
||||
}
|
||||
let applicationListAllFilter = applicationListAll.value.filter((item) => {
|
||||
return transferValue.value.some((id) => String(id) === String(item.adviceDefinitionId));
|
||||
return transferValue.value.includes(item.adviceDefinitionId);
|
||||
});
|
||||
applicationListAllFilter = applicationListAllFilter.map((item) => {
|
||||
const priceInfo = item.priceList?.[0] || {};
|
||||
return {
|
||||
adviceDefinitionId: item.adviceDefinitionId /** 诊疗定义id */,
|
||||
quantity: 1, // /** 请求数量 */
|
||||
unitCode: priceInfo.unitCode /** 请求单位编码 */,
|
||||
unitPrice: priceInfo.price /** 单价 */,
|
||||
totalPrice: priceInfo.price /** 总价 */,
|
||||
positionId: form.targetDepartment || item.positionId, //执行科室id
|
||||
unitCode: item.priceList[0].unitCode /** 请求单位编码 */,
|
||||
unitPrice: item.priceList[0].price /** 单价 */,
|
||||
totalPrice: item.priceList[0].price /** 总价 */,
|
||||
positionId: item.positionId, //执行科室id
|
||||
ybClassEnum: item.ybClassEnum, //类别医保编码
|
||||
conditionId: item.conditionId, //诊断ID
|
||||
encounterDiagnosisId: item.encounterDiagnosisId, //就诊诊断id
|
||||
adviceType: item.adviceType, ///** 医嘱类型 */
|
||||
definitionId: priceInfo.definitionId, //费用定价主表ID */
|
||||
definitionId: item.priceList[0].definitionId, //费用定价主表ID */
|
||||
definitionDetailId: item.definitionDetailId, //费用定价子表ID */
|
||||
accountId: patientInfo.value.accountId, // // 账户id
|
||||
};
|
||||
@@ -478,22 +254,16 @@ const submit = async () => {
|
||||
if (res.code === 200) {
|
||||
proxy.$message.success(res.msg);
|
||||
applicationList.value = [];
|
||||
applicationListAll.value = [];
|
||||
transferValue.value = [];
|
||||
lastValidTransferValue.value = [];
|
||||
emits('submitOk');
|
||||
} else {
|
||||
proxy.$message.error(res.message);
|
||||
}
|
||||
});
|
||||
};
|
||||
/** 查询科室(与检验申请单一致) */
|
||||
/** 查询科室 */
|
||||
const getLocationInfo = () => {
|
||||
return getDepartmentList().then((res) => {
|
||||
orgOptions.value = normalizeOrgTreeIds(res?.data || []);
|
||||
if (transferValue.value.length > 0) {
|
||||
nextTick(() => fillTargetDepartmentFromSelection(transferValue.value, 1));
|
||||
}
|
||||
getDepartmentList().then((res) => {
|
||||
orgOptions.value = res.data || [];
|
||||
});
|
||||
};
|
||||
// 获取诊断目录
|
||||
@@ -530,7 +300,7 @@ function getDiagnosisList() {
|
||||
}
|
||||
});
|
||||
}
|
||||
defineExpose({ state, submit, getLocationInfo, getDiagnosisList, getList });
|
||||
defineExpose({ state, submit, getLocationInfo, getDiagnosisList });
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.bloodTransfusion-container {
|
||||
@@ -542,22 +312,8 @@ defineExpose({ state, submit, getLocationInfo, getDiagnosisList, getList });
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
:deep(.el-transfer) {
|
||||
.el-transfer {
|
||||
--el-transfer-panel-width: 480px !important;
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
}
|
||||
|
||||
:deep(.el-transfer__buttons) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
:deep(.el-transfer__button) {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.bloodTransfusion-form {
|
||||
|
||||
@@ -17,14 +17,17 @@
|
||||
style="width: 300px; margin-bottom: 10px"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="handleSearch" :loading="loading">搜索</el-button>
|
||||
<el-button @click="handleSearch">搜索</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<span class="total-count">共 {{ totalCount }} 项</span>
|
||||
<span v-if="!searchKey" class="total-count">共 {{ totalCount }} 项</span>
|
||||
<span v-else class="total-count">搜索到 {{ filteredCount }} 项 / 共 {{ totalCount }} 项</span>
|
||||
</div>
|
||||
<el-transfer
|
||||
v-model="transferValue"
|
||||
:data="transferData"
|
||||
filter-placeholder="项目代码/名称"
|
||||
filterable
|
||||
:titles="['未选择', '已选择']"
|
||||
/>
|
||||
</div>
|
||||
@@ -133,8 +136,7 @@
|
||||
<script setup name="LaboratoryTests">
|
||||
import {getCurrentInstance, nextTick, onMounted, reactive, ref, watch, computed} from 'vue';
|
||||
import {patientInfo} from '../../../store/patient.js';
|
||||
import {getExaminationPage, saveInspection} from './api';
|
||||
import {ActivityCategory} from '@/utils/medicalConstants';
|
||||
import {getApplicationList, saveInspection} from './api';
|
||||
import {getDepartmentList} from '@/api/public.js';
|
||||
import {getEncounterDiagnosis} from '../../api.js';
|
||||
import {ElMessage} from 'element-plus';
|
||||
@@ -171,8 +173,9 @@ const skipDeptAutoFill = ref(false);
|
||||
// 将已加载的全部数据转为 transfer 组件所需的格式
|
||||
const buildTransferData = (records) => {
|
||||
return records.map((item) => {
|
||||
const price = item.price != null ? Number(item.price).toFixed(2) : '0.00';
|
||||
const unit = item.unitCodeDictText || item.unitCode || '';
|
||||
const priceInfo = item.priceList?.[0] || {};
|
||||
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
|
||||
const unit = item.unitCode_dictText || item.unitCode || '';
|
||||
return {
|
||||
adviceDefinitionId: item.adviceDefinitionId,
|
||||
orgId: item.orgId,
|
||||
@@ -182,8 +185,7 @@ const buildTransferData = (records) => {
|
||||
});
|
||||
};
|
||||
|
||||
const selectedItemsCache = ref(new Map());
|
||||
|
||||
// 加载全部数据(不分页,一次性拉取)
|
||||
const loadAllData = async () => {
|
||||
if (!patientInfo.value?.inHospitalOrgId) {
|
||||
applicationListAll.value = [];
|
||||
@@ -191,12 +193,13 @@ const loadAllData = async () => {
|
||||
}
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getExaminationPage({
|
||||
pageSize: 100,
|
||||
// 使用大 pageSize 一次性拉取所有启用状态的检验类诊疗项目
|
||||
const res = await getApplicationList({
|
||||
pageSize: 9999,
|
||||
pageNo: 1,
|
||||
categoryCode: ActivityCategory.PROOF,
|
||||
categoryCode: '22',
|
||||
organizationId: patientInfo.value.inHospitalOrgId,
|
||||
searchKey: searchKey.value,
|
||||
adviceTypes: [3], // 1 药品 2 耗材 3 诊疗
|
||||
});
|
||||
if (res.code !== 200) {
|
||||
proxy.$message.error(res.message);
|
||||
@@ -205,9 +208,8 @@ const loadAllData = async () => {
|
||||
}
|
||||
applicationListAll.value = res.data?.records || [];
|
||||
totalCount.value = res.data?.total || 0;
|
||||
if (!searchKey.value) {
|
||||
applyEditTransferSelection();
|
||||
}
|
||||
// 检验项目列表为异步加载,编辑回显必须在数据就绪后执行,否则已选区一直为空
|
||||
applyEditTransferSelection()
|
||||
} catch (e) {
|
||||
proxy.$message.error('获取检验项目列表失败');
|
||||
applicationListAll.value = [];
|
||||
@@ -216,18 +218,32 @@ const loadAllData = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const transferData = computed(() => buildTransferData(applicationListAll.value));
|
||||
// 根据搜索关键词过滤数据
|
||||
const filterData = (key) => {
|
||||
if (!key || key.trim() === '') {
|
||||
return applicationListAll.value;
|
||||
}
|
||||
const lowerKey = key.toLowerCase().trim();
|
||||
return applicationListAll.value.filter((item) => {
|
||||
return (
|
||||
item.adviceName?.toLowerCase().includes(lowerKey) ||
|
||||
item.pyStr?.toLowerCase().includes(lowerKey) ||
|
||||
item.adviceBusNo?.toLowerCase().includes(lowerKey)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// transfer 组件实际显示的数据(受搜索词影响)
|
||||
const transferData = computed(() => buildTransferData(filterData(searchKey.value)));
|
||||
// 当前显示的条数
|
||||
const filteredCount = computed(() => filterData(searchKey.value).length);
|
||||
|
||||
const getList = async () => {
|
||||
await loadAllData();
|
||||
};
|
||||
|
||||
let searchTimer = null;
|
||||
const handleSearch = () => {
|
||||
clearTimeout(searchTimer);
|
||||
searchTimer = setTimeout(() => {
|
||||
loadAllData();
|
||||
}, 300);
|
||||
// 搜索时保持已选中的项目不受影响
|
||||
};
|
||||
// 编辑初始化标志:避免 applyEditTransferSelection 设置 transferValue 时触发 projectWithDepartment 覆盖 descJson 中的科室值
|
||||
const isInitializing = ref(false);
|
||||
@@ -286,17 +302,13 @@ const projectWithDepartment = (selectProjectIds, type) => {
|
||||
const arr = [];
|
||||
// 根据选中的项目id查找对应的项目(从全部原始数据中查找)
|
||||
selectProjectIds.forEach((element) => {
|
||||
let searchData = applicationListAll.value.find((item) => {
|
||||
const searchData = applicationListAll.value.find((item) => {
|
||||
return element == item.adviceDefinitionId;
|
||||
});
|
||||
if (!searchData) {
|
||||
searchData = selectedItemsCache.value.get(element);
|
||||
}
|
||||
if (searchData) {
|
||||
const priceInfo = searchData.priceList?.[0] || {};
|
||||
const price = searchData.price != null ? Number(searchData.price).toFixed(2)
|
||||
: priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
|
||||
const unit = searchData.unitCodeDictText || searchData.unitCode_dictText || searchData.unitCode || '';
|
||||
const price = priceInfo.price != null ? Number(priceInfo.price).toFixed(2) : '0.00';
|
||||
const unit = searchData.unitCode_dictText || searchData.unitCode || '';
|
||||
arr.push({
|
||||
adviceDefinitionId: searchData.adviceDefinitionId,
|
||||
orgId: searchData.orgId,
|
||||
@@ -359,12 +371,6 @@ watch(
|
||||
(newValue) => {
|
||||
if (skipDeptAutoFill.value) return;
|
||||
if (isInitializing.value) return;
|
||||
newValue.forEach((id) => {
|
||||
if (!selectedItemsCache.value.has(id)) {
|
||||
const item = applicationListAll.value.find((i) => i.adviceDefinitionId == id);
|
||||
if (item) selectedItemsCache.value.set(id, item);
|
||||
}
|
||||
});
|
||||
projectWithDepartment(newValue, 1);
|
||||
}
|
||||
);
|
||||
@@ -449,14 +455,13 @@ watch(
|
||||
}
|
||||
)
|
||||
|
||||
// 编辑模式下,项目字典首次加载完成后回显已选项目(搜索刷新不重置)
|
||||
// 编辑模式下,项目字典加载完成后重新回显已选项目
|
||||
watch(
|
||||
() => applicationListAll.value,
|
||||
() => {
|
||||
if (!props.editData?.requestFormId) return;
|
||||
if (!props.editData.requestFormDetailList?.length) return;
|
||||
if (!applicationListAll.value.length) return;
|
||||
if (searchKey.value) return;
|
||||
|
||||
const selectedIds = [];
|
||||
props.editData.requestFormDetailList.forEach((detail) => {
|
||||
@@ -481,29 +486,26 @@ const submit = () => {
|
||||
if (!projectWithDepartment(transferValue.value, 2)) {
|
||||
return;
|
||||
}
|
||||
let applicationListAllFilter = transferValue.value.map((id) => {
|
||||
let item = applicationListAll.value.find((i) => i.adviceDefinitionId == id);
|
||||
if (!item) {
|
||||
item = selectedItemsCache.value.get(id);
|
||||
}
|
||||
if (!item) return null;
|
||||
const priceInfo = item.priceList?.[0] || {};
|
||||
let applicationListAllFilter = applicationListAll.value.filter((item) => {
|
||||
return transferValue.value.includes(item.adviceDefinitionId);
|
||||
});
|
||||
applicationListAllFilter = applicationListAllFilter.map((item) => {
|
||||
return {
|
||||
adviceDefinitionId: item.adviceDefinitionId /** 诊疗定义id */,
|
||||
quantity: 1, // /** 请求数量 */
|
||||
unitCode: item.unitCode || priceInfo.unitCode || '' /** 请求单位编码 */,
|
||||
unitPrice: item.price ?? priceInfo.price ?? 0 /** 单价 */,
|
||||
totalPrice: item.price ?? priceInfo.price ?? 0 /** 总价 */,
|
||||
unitCode: item.priceList[0].unitCode /** 请求单位编码 */,
|
||||
unitPrice: item.priceList[0].price /** 单价 */,
|
||||
totalPrice: item.priceList[0].price /** 总价 */,
|
||||
positionId: form.targetDepartment || item.positionId, // 用户指定发往科室优先于项目默认执行科室
|
||||
ybClassEnum: item.ybClassEnum || '', //类别医保编码
|
||||
conditionId: item.conditionId || '', //诊断ID
|
||||
encounterDiagnosisId: item.encounterDiagnosisId || '', //就诊诊断id
|
||||
adviceType: item.adviceType || 3, ///** 医嘱类型 */
|
||||
definitionId: item.chargeItemDefinitionId || priceInfo.definitionId || '', //费用定价主表ID */
|
||||
definitionDetailId: item.definitionDetailId || priceInfo.definitionDetailId || '', //费用定价子表ID */
|
||||
ybClassEnum: item.ybClassEnum, //类别医保编码
|
||||
conditionId: item.conditionId, //诊断ID
|
||||
encounterDiagnosisId: item.encounterDiagnosisId, //就诊诊断id
|
||||
adviceType: item.adviceType, ///** 医嘱类型 */
|
||||
definitionId: item.priceList[0].definitionId, //费用定价主表ID */
|
||||
definitionDetailId: item.definitionDetailId, //费用定价子表ID */
|
||||
accountId: patientInfo.value.accountId, // // 账户id
|
||||
};
|
||||
}).filter(Boolean);
|
||||
});
|
||||
const params = {
|
||||
activityList: applicationListAllFilter,
|
||||
patientId: patientInfo.value.patientId, //患者ID
|
||||
@@ -518,7 +520,6 @@ const submit = () => {
|
||||
if (res.code === 200) {
|
||||
proxy.$message.success(isEditMode.value ? '修改成功' : res.msg);
|
||||
transferValue.value = [];
|
||||
selectedItemsCache.value.clear();
|
||||
emits('submitOk');
|
||||
} else {
|
||||
proxy.$message.error(res.message);
|
||||
|
||||
@@ -207,7 +207,6 @@ import {patientInfo} from '../../../store/patient.js';
|
||||
import {getDepartmentList} from '@/api/public.js';
|
||||
import {getEncounterDiagnosis} from '../../api.js';
|
||||
import {getExaminationPage, saveCheckd} from './api';
|
||||
import {ActivityCategory} from '@/utils/medicalConstants';
|
||||
import {ElMessage, ElMessageBox} from 'element-plus';
|
||||
import {WarningFilled, Warning, Refresh, Files, Document, EditPen, Aim, DocumentCopy} from '@element-plus/icons-vue';
|
||||
|
||||
@@ -277,7 +276,6 @@ const getList = () => {
|
||||
pageNo: 1,
|
||||
pageSize: 5000,
|
||||
searchKey: '',
|
||||
categoryCode: ActivityCategory.TEST,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 200 && res.data?.records) {
|
||||
|
||||
@@ -198,7 +198,7 @@
|
||||
v-model="scope.row.adviceName"
|
||||
placeholder="请选择项目"
|
||||
@input="handleChange"
|
||||
@focus="handleFocus(scope.row, scope.$index)"
|
||||
@click="handleFocus(scope.row, scope.$index)"
|
||||
@keyup.enter.stop="handleFocus(scope.row, scope.$index)"
|
||||
@keydown="
|
||||
(e) => {
|
||||
@@ -640,10 +640,6 @@ function getListInfo(addNewRow) {
|
||||
};
|
||||
})
|
||||
.sort((a, b) => {
|
||||
// 没有 requestTime 的项(新增/组套添加)排在最前面
|
||||
if (!a.requestTime && !b.requestTime) return 0;
|
||||
if (!a.requestTime) return -1;
|
||||
if (!b.requestTime) return 1;
|
||||
return new Date(b.requestTime) - new Date(a.requestTime);
|
||||
});
|
||||
getGroupMarkers(); // 更新标记
|
||||
@@ -900,21 +896,31 @@ function handleDiagnosisChange(item) {
|
||||
function handleFocus(row, index) {
|
||||
rowIndex.value = index;
|
||||
row.showPopover = true;
|
||||
// Bug #555: handleFocus 只负责开 popover 和初始化查询参数,搜索由 handleChange 统一处理
|
||||
// 避免异步 refresh 用旧闭包 searchKey 覆盖 handleChange 的搜索结果
|
||||
const adviceType = row.adviceType !== undefined ? row.adviceType : adviceQueryParams.value.adviceType;
|
||||
let categoryCode = '';
|
||||
if (row.adviceType !== undefined) {
|
||||
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);
|
||||
categoryCode = selectedItem ? selectedItem.categoryCode : (row.categoryCode || '');
|
||||
}
|
||||
adviceQueryParams.value = { adviceType, categoryCode, searchKey: '' };
|
||||
// handleFocus 打开 popover 时也要加载数据
|
||||
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[index] : adviceTableRef.value;
|
||||
if (tableRef && tableRef.refresh) {
|
||||
tableRef.refresh(adviceType, categoryCode, '');
|
||||
}
|
||||
// 用 adviceType + categoryCode 组合查找匹配的选项
|
||||
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);
|
||||
// If the row has an explicit adviceType (saved/existing row), use its own categoryCode.
|
||||
// If no type is selected (new row), use empty string for global search across all categories.
|
||||
const categoryCode = selectedItem ? selectedItem.categoryCode : (row.adviceType != null ? (row.categoryCode || '') : '');
|
||||
const searchKey = row.adviceName || '';
|
||||
|
||||
nextTick(() => {
|
||||
nextTick(() => {
|
||||
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[index] : adviceTableRef.value;
|
||||
if (tableRef && tableRef.refresh) {
|
||||
tableRef.refresh(adviceType, categoryCode, searchKey);
|
||||
} else {
|
||||
// fallback: 如果双重 nextTick 仍未挂载,延迟 100ms 再试
|
||||
setTimeout(() => {
|
||||
const tableRef2 = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[index] : adviceTableRef.value;
|
||||
if (tableRef2 && tableRef2.refresh) {
|
||||
tableRef2.refresh(adviceType, categoryCode, searchKey);
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function handleBlur(row) {
|
||||
@@ -923,24 +929,20 @@ function handleBlur(row) {
|
||||
|
||||
function handleChange(value) {
|
||||
adviceQueryParams.value.searchKey = value;
|
||||
// @focus 已先于 @input 执行,rowIndex 必定有效
|
||||
const currentIndex = rowIndex.value;
|
||||
if (currentIndex < 0) return;
|
||||
const row = filterPrescriptionList.value[currentIndex];
|
||||
// popover 被 blur 关闭后,用户继续输入时自行打开
|
||||
if (!row.showPopover) {
|
||||
row.showPopover = true;
|
||||
}
|
||||
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[currentIndex] : adviceTableRef.value;
|
||||
if (tableRef && tableRef.refresh) {
|
||||
const adviceType = row?.adviceType !== undefined ? row.adviceType : adviceQueryParams.value.adviceType;
|
||||
let categoryCode = '';
|
||||
if (row?.adviceType !== undefined) {
|
||||
// 搜索词变化时,调用当前行子组件的 refresh 方法
|
||||
const index = rowIndex.value;
|
||||
if (index >= 0) {
|
||||
const tableRef = Array.isArray(adviceTableRef.value) ? adviceTableRef.value[index] : adviceTableRef.value;
|
||||
if (tableRef && tableRef.refresh) {
|
||||
const row = filterPrescriptionList.value[index];
|
||||
const adviceType = row?.adviceType !== undefined ? row.adviceType : adviceQueryParams.value.adviceType;
|
||||
// 用 adviceType + categoryCode 组合查找匹配的选项
|
||||
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);
|
||||
categoryCode = selectedItem ? selectedItem.categoryCode : (adviceQueryParams.value.categoryCode || '');
|
||||
// 修复Bug #486:当行没有显式选择医嘱类型时,不传categoryCode,让搜索在全药库中进行
|
||||
const categoryCode = selectedItem ? selectedItem.categoryCode : (row?.adviceType !== undefined ? (adviceQueryParams.value.categoryCode || '') : '');
|
||||
tableRef.refresh(adviceType, categoryCode, value);
|
||||
}
|
||||
tableRef.refresh(adviceType, categoryCode, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1577,24 +1579,11 @@ function handleSaveGroup(orderGroupList) {
|
||||
|
||||
let successCount = 0;
|
||||
|
||||
// 收集所有要添加的新行,最后统一 unshift 到数组开头(置顶显示)
|
||||
const newRows = [];
|
||||
|
||||
// 记录循环前的数组长度,用于清理循环中创建的临时行
|
||||
const originalLength = prescriptionList.value.length;
|
||||
|
||||
orderGroupList.forEach((item) => {
|
||||
// 使用临时索引,先追加到末尾用于 setValue 填充
|
||||
const tempIndex = prescriptionList.value.length;
|
||||
prescriptionList.value[tempIndex] = {
|
||||
uniqueKey: nextId.value++,
|
||||
isEdit: false,
|
||||
statusEnum: 1,
|
||||
};
|
||||
rowIndex.value = prescriptionList.value.length;
|
||||
|
||||
if (!item) {
|
||||
console.warn('组套中的项目为空');
|
||||
prescriptionList.value.splice(tempIndex, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1620,12 +1609,18 @@ function handleSaveGroup(orderGroupList) {
|
||||
therapyEnum: item.orderDetailInfos?.therapyEnum || '1',
|
||||
};
|
||||
|
||||
rowIndex.value = tempIndex;
|
||||
// 预初始化空行(组套项带预填值,设为 false 让明细字段在表格中直接展示)
|
||||
prescriptionList.value[rowIndex.value] = {
|
||||
uniqueKey: nextId.value++,
|
||||
isEdit: false,
|
||||
statusEnum: 1,
|
||||
};
|
||||
|
||||
setValue(mergedDetail);
|
||||
|
||||
// 创建新的处方项目
|
||||
const newRow = {
|
||||
...prescriptionList.value[tempIndex],
|
||||
...prescriptionList.value[rowIndex.value],
|
||||
patientId: patientInfo.value.patientId,
|
||||
encounterId: patientInfo.value.encounterId,
|
||||
accountId: accountId.value,
|
||||
@@ -1644,12 +1639,12 @@ function handleSaveGroup(orderGroupList) {
|
||||
orgId: resolveOrgId(mergedDetail.orgId || patientInfo.value?.inHospitalOrgId) || '',
|
||||
// 🔧 修复:同时存储 orgName,确保树匹配不到时仍有中文名称可显示
|
||||
orgName: findOrgName(mergedDetail.orgId || patientInfo.value?.inHospitalOrgId) || mergedDetail.orgName || patientInfo.value?.inHospitalOrgName || '',
|
||||
dbOpType: prescriptionList.value[tempIndex].requestId ? '2' : '1',
|
||||
dbOpType: prescriptionList.value[rowIndex.value].requestId ? '2' : '1',
|
||||
conditionId: conditionId.value,
|
||||
conditionDefinitionId: conditionDefinitionId.value,
|
||||
encounterDiagnosisId: encounterDiagnosisId.value,
|
||||
diagnosisName: diagnosisName.value,
|
||||
therapyEnum: prescriptionList.value[tempIndex]?.therapyEnum || mergedDetail.therapyEnum || '1',
|
||||
therapyEnum: prescriptionList.value[rowIndex.value]?.therapyEnum || mergedDetail.therapyEnum || '1',
|
||||
// 🔧 修复:确保组套医嘱的 categoryEnum 被正确映射,防止后端 NPE
|
||||
categoryEnum: mergedDetail?.categoryEnum || mergedDetail?.categoryCode || item?.categoryCode,
|
||||
};
|
||||
@@ -1668,14 +1663,11 @@ function handleSaveGroup(orderGroupList) {
|
||||
}
|
||||
|
||||
newRow.contentJson = JSON.stringify(newRow);
|
||||
newRows.push(newRow);
|
||||
prescriptionList.value[rowIndex.value] = newRow;
|
||||
successCount++;
|
||||
});
|
||||
|
||||
// 清理循环中创建的临时行,统一添加到数组开头(置顶显示)
|
||||
if (newRows.length > 0) {
|
||||
prescriptionList.value.splice(originalLength); // 移除循环中追加到末尾的临时行
|
||||
prescriptionList.value.unshift(...newRows);
|
||||
if (successCount > 0) {
|
||||
proxy.$modal.msgSuccess(`成功添加 ${successCount} 个医嘱项`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,8 +46,7 @@
|
||||
<div style="display: flex; gap: 20px; height: 70vh">
|
||||
<div
|
||||
style="
|
||||
width: 350px;
|
||||
min-width: 350px;
|
||||
width: 250px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
@@ -71,35 +70,21 @@
|
||||
<span class="status-dot"></span>
|
||||
{{ getItemType_Text(item.adviceType) }}
|
||||
</div>
|
||||
<el-tooltip :content="item.adviceName" placement="top" :show-after="500">
|
||||
<div class="item-name">{{ item.adviceName }}</div>
|
||||
</el-tooltip>
|
||||
<el-tooltip
|
||||
:content="
|
||||
<div class="item-name">{{ item.adviceName }}</div>
|
||||
<div class="item-name">
|
||||
{{
|
||||
item.priceList && item.priceList.length > 0
|
||||
? (item.priceList[0].price / item.partPercent).toFixed(2) + '元/' + item.minUnitCode_dictText
|
||||
? (item.priceList[0].price / item.partPercent).toFixed(2) +
|
||||
'元' +
|
||||
'/' +
|
||||
item.minUnitCode_dictText
|
||||
: ''
|
||||
"
|
||||
placement="top"
|
||||
:show-after="500"
|
||||
>
|
||||
<div class="item-name">
|
||||
{{
|
||||
item.priceList && item.priceList.length > 0
|
||||
? (item.priceList[0].price / item.partPercent).toFixed(2) +
|
||||
'元' +
|
||||
'/' +
|
||||
item.minUnitCode_dictText
|
||||
: ''
|
||||
}}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="item.adviceType === 2" :content="'库存数量:' + handleQuantity(item)" placement="top" :show-after="500">
|
||||
<div class="item-name">
|
||||
库存数量:
|
||||
{{ handleQuantity(item) }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
}}
|
||||
</div>
|
||||
<div class="item-name" v-if="item.adviceType === 2">
|
||||
库存数量:
|
||||
{{ handleQuantity(item) }}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 只显示暂无数据文本 -->
|
||||
<div
|
||||
@@ -323,7 +308,7 @@
|
||||
import {computed, getCurrentInstance, onMounted, reactive, ref, watch} from 'vue';
|
||||
import {ElMessage} from 'element-plus';
|
||||
import {formatDateStr} from '@/utils/index';
|
||||
import {getAdviceBaseInfo, getDiseaseTreatmentInitLoc, getOrgList, getOrgLocConfig} from './api.js';
|
||||
import {getAdviceBaseInfo, getDiseaseTreatmentInitLoc, getOrgList} from './api.js';
|
||||
import {getOrderGroup} from '@/views/doctorstation/components/api.js';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
|
||||
@@ -381,7 +366,6 @@ const executeTime = ref('');
|
||||
const departmentOptions = ref([]);
|
||||
const AdviceBaseInfoList = ref([]);
|
||||
const locationOptions = ref([]);
|
||||
const consumableDefaultLocId = ref(null); // 患者科室耗材默认库房ID(来自取药科室配置)
|
||||
const searchText = ref('');
|
||||
const userId = ref('');
|
||||
const orgId = ref('');
|
||||
@@ -487,8 +471,11 @@ onMounted(() => {
|
||||
const userStore = useUserStore();
|
||||
userId.value = userStore.id;
|
||||
orgId.value = userStore.orgId;
|
||||
console.log(props.patientInfo, 'patientInfo in FeeDialog');
|
||||
console.log('initialData in FeeDialog');
|
||||
// 数据加载由 watch(visible) 统一触发,避免 patientInfo 未就绪时调用报错
|
||||
loadDepartmentOptions();
|
||||
getAdviceBaseInfos();
|
||||
getDiseaseInitLoc();
|
||||
});
|
||||
|
||||
// 监听弹窗显示状态
|
||||
@@ -497,12 +484,10 @@ watch(
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
executeTime.value = formatDateStr(new Date(), 'YYYY-MM-DD HH:mm:ss');
|
||||
consumableDefaultLocId.value = null; // 重置耗材默认库房,避免复用上次患者配置
|
||||
// 弹窗打开时按当前患者科室重新加载,避免复用上一次患者/登录科室的结果
|
||||
loadDepartmentOptions();
|
||||
getAdviceBaseInfos();
|
||||
getDiseaseInitLoc(16);
|
||||
loadConsumableDefaultLoc();
|
||||
} else {
|
||||
resetData();
|
||||
}
|
||||
@@ -531,16 +516,7 @@ watch(
|
||||
if (!locs || locs.length === 0) return;
|
||||
feeItemsList.value.forEach(item => {
|
||||
if (item.adviceType === 2 && !item.positionId) {
|
||||
if (consumableDefaultLocId.value) {
|
||||
const matched = locs.find(d => String(d.value) === consumableDefaultLocId.value);
|
||||
if (matched) {
|
||||
item.positionId = String(matched.value);
|
||||
} else {
|
||||
ElMessage.warning(`"${item.adviceName}" 未找到匹配的执行科室,请手动选择`);
|
||||
}
|
||||
} else {
|
||||
ElMessage.warning(`"${item.adviceName}" 所在科室未配置耗材执行科室,请手动选择`);
|
||||
}
|
||||
item.positionId = String(locs[0].value);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -610,33 +586,14 @@ function getAdviceBaseInfos() {
|
||||
});
|
||||
}
|
||||
function getDiseaseInitLoc() {
|
||||
// 16=药房,17=耗材库,合并后作为耗材执行科室下拉选项
|
||||
Promise.all([
|
||||
getDiseaseTreatmentInitLoc(16).catch(() => ({ data: { locationOptions: [] } })),
|
||||
getDiseaseTreatmentInitLoc(17).catch(() => ({ data: { locationOptions: [] } })),
|
||||
]).then(([pharmacyRes, warehouseRes]) => {
|
||||
const pharmacies = pharmacyRes.data?.locationOptions || [];
|
||||
const warehouses = warehouseRes.data?.locationOptions || [];
|
||||
locationOptions.value = [...pharmacies, ...warehouses];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询患者科室的耗材默认库房(取药科室配置 itemCode=2)
|
||||
*/
|
||||
function loadConsumableDefaultLoc() {
|
||||
const deptId = props.patientInfo?.organizationId;
|
||||
if (!deptId) {
|
||||
consumableDefaultLocId.value = null;
|
||||
return;
|
||||
}
|
||||
getOrgLocConfig({ organizationId: deptId, itemCode: '2', pageNo: 1, pageSize: 100 })
|
||||
.then((res) => {
|
||||
const records = res.data?.records || [];
|
||||
consumableDefaultLocId.value = records.length > 0 ? String(records[0].defLocationId) : null;
|
||||
getDiseaseTreatmentInitLoc(16)
|
||||
.then((response) => {
|
||||
console.log('Disease Treatment Init Loc:', response);
|
||||
locationOptions.value = response.data.locationOptions;
|
||||
})
|
||||
.catch(() => {
|
||||
consumableDefaultLocId.value = null;
|
||||
console.warn('位置列表加载失败(可能无权限)');
|
||||
locationOptions.value = [];
|
||||
});
|
||||
}
|
||||
// 下拉框模糊搜索过滤(自定义filter-method,配合element-plus filterable使用)
|
||||
@@ -787,19 +744,8 @@ function selectChange(row) {
|
||||
defaultPositionId = String(departmentOptions.value[0].id);
|
||||
}
|
||||
} else if (row.adviceType === 2 && locationOptions.value.length > 0) {
|
||||
// 耗材:必须从取药科室配置中匹配默认库房,未配置则提示用户
|
||||
if (consumableDefaultLocId.value) {
|
||||
const matched = locationOptions.value.find(
|
||||
d => String(d.value) === consumableDefaultLocId.value
|
||||
);
|
||||
if (matched) {
|
||||
defaultPositionId = String(matched.value);
|
||||
} else {
|
||||
ElMessage.warning(`"${row.adviceName}" 未找到匹配的执行科室,请手动选择`);
|
||||
}
|
||||
} else {
|
||||
ElMessage.warning(`"${row.adviceName}" 所在科室未配置耗材执行科室,请手动选择`);
|
||||
}
|
||||
// 耗材:默认取第一个药房/耗材房
|
||||
defaultPositionId = String(locationOptions.value[0].value);
|
||||
}
|
||||
//插入费用列表
|
||||
feeItemsList.value.push({
|
||||
@@ -1077,8 +1023,6 @@ function applyGroupSet() {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 8px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -111,16 +111,6 @@ export function getDiseaseTreatmentInitLoc(id) {
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 查询科室取药配置(耗材默认库房)
|
||||
*/
|
||||
export function getOrgLocConfig(params) {
|
||||
return request({
|
||||
url: '/base-data-manage/org-loc/org-loc',
|
||||
method: 'get',
|
||||
params: params,
|
||||
});
|
||||
}
|
||||
// 住院护士站费用明细
|
||||
export function getCostDetail(queryParams) {
|
||||
return request({
|
||||
|
||||
@@ -90,13 +90,6 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="医嘱状态" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusType(scope.row)" size="small">
|
||||
{{ getStatusDisplayText(scope.row) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="医嘱内容" prop="adviceName">
|
||||
<template #default="scope">
|
||||
<span>
|
||||
@@ -176,43 +169,6 @@ import {formatDateStr} from '@/utils/index';
|
||||
import {getCurrentInstance, ref} from 'vue';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
|
||||
/** 发药状态 → 医嘱状态(药品医嘱状态映射表) */
|
||||
const DISPENSE_STATUS_TO_ADVICE_TEXT = {
|
||||
2: '已执行',
|
||||
8: '已提交',
|
||||
4: '已发药',
|
||||
};
|
||||
|
||||
function getStatusDisplayText(row) {
|
||||
const params = row?.medicineSummaryParamList || [];
|
||||
const pending = params.filter((p) => Number(p.dispenseStatus) !== 8);
|
||||
if (pending.length === 0) {
|
||||
return params.length ? '已提交' : '已执行';
|
||||
}
|
||||
const texts = [
|
||||
...new Set(
|
||||
pending
|
||||
.map((p) => DISPENSE_STATUS_TO_ADVICE_TEXT[Number(p.dispenseStatus)])
|
||||
.filter(Boolean),
|
||||
),
|
||||
];
|
||||
if (texts.length === 1) {
|
||||
return texts[0];
|
||||
}
|
||||
return '已执行';
|
||||
}
|
||||
|
||||
function getStatusType(row) {
|
||||
const text = getStatusDisplayText(row);
|
||||
if (text === '已发药') {
|
||||
return 'success';
|
||||
}
|
||||
if (text === '已提交') {
|
||||
return 'warning';
|
||||
}
|
||||
return 'primary';
|
||||
}
|
||||
|
||||
const activeNames = ref([]);
|
||||
|
||||
const userStore = useUserStore();
|
||||
@@ -334,7 +290,6 @@ function handleMedicineSummary() {
|
||||
medicineSummary(ids).then((res) => {
|
||||
if (res.code == 200) {
|
||||
proxy.$message.success('操作成功');
|
||||
handleGetPrescription();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,13 +16,7 @@
|
||||
<el-table-column label="药房" align="center" prop="locationName" />
|
||||
<el-table-column label="申请人" align="center" prop="applicantName" />
|
||||
<el-table-column label="领药人" align="center" prop="receiverName" />
|
||||
<el-table-column label="医嘱状态" align="center" prop="statusEnum_enumText">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getSummaryStatusType(scope.row)" size="small">
|
||||
{{ formatSummaryStatusText(scope.row) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="发药状态" align="center" prop="statusEnum_enumText" />
|
||||
<el-table-column label="操作" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="getDetail(scope.row)">详情</el-button>
|
||||
@@ -58,35 +52,6 @@ import {getMedicineSummary, getMedicineSummaryDetail} from './api';
|
||||
import {patientInfoList} from '../../components/store/patient.js';
|
||||
import {getCurrentInstance, ref} from 'vue';
|
||||
|
||||
const SUMMARY_STATUS_DISPLAY = {
|
||||
2: '已提交',
|
||||
4: '已发药',
|
||||
};
|
||||
|
||||
const LEGACY_SUMMARY_STATUS_TEXT = {
|
||||
待配药: '已提交',
|
||||
已发放: '已发药',
|
||||
};
|
||||
|
||||
function formatSummaryStatusText(row) {
|
||||
const code = Number(row?.statusEnum);
|
||||
if (SUMMARY_STATUS_DISPLAY[code]) {
|
||||
return SUMMARY_STATUS_DISPLAY[code];
|
||||
}
|
||||
return LEGACY_SUMMARY_STATUS_TEXT[row?.statusEnum_enumText] || row?.statusEnum_enumText || '-';
|
||||
}
|
||||
|
||||
function getSummaryStatusType(row) {
|
||||
const text = formatSummaryStatusText(row);
|
||||
if (text === '已发药') {
|
||||
return 'success';
|
||||
}
|
||||
if (text === '已提交') {
|
||||
return 'warning';
|
||||
}
|
||||
return 'info';
|
||||
}
|
||||
|
||||
const medicineSummaryFormList = ref([]);
|
||||
const medicineSummaryFormDetails = ref([]);
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
@@ -184,11 +184,6 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="开始/终止" prop="requestTime" width="200" />
|
||||
<el-table-column label="医嘱状态" align="center" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="props.exeStatus === 6" type="success" size="small">已执行</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="props.exeStatus == 1 ? '预计执行' : '执行时间'" prop="times">
|
||||
<template #default="scope">
|
||||
<div
|
||||
@@ -234,80 +229,8 @@ import {adviceCancel, adviceExecute, adviceNoExecute, getPrescriptionList} from
|
||||
import {patientInfoList} from '../../components/store/patient.js';
|
||||
import {lotNumberMatch} from '@/api/public';
|
||||
import {formatDateStr} from '@/utils/index';
|
||||
import {RequestStatus} from '@/utils/medicalConstants';
|
||||
import {getCurrentInstance, nextTick, ref, provide} from 'vue';
|
||||
|
||||
/** 请求状态 → 医嘱状态映射表(开具/签发/校对) */
|
||||
const REQUEST_STATUS_DISPLAY = {
|
||||
[RequestStatus.DRAFT]: '待签发',
|
||||
[RequestStatus.ACTIVE]: '已签发',
|
||||
[RequestStatus.COMPLETED]: '已校对',
|
||||
};
|
||||
|
||||
/** 发药状态 → 医嘱状态映射表(汇总申请/发药) */
|
||||
const DISPENSE_STATUS_DISPLAY = {
|
||||
8: '已提交',
|
||||
4: '已发药',
|
||||
};
|
||||
|
||||
/** 执行页签对应的医嘱状态展示 */
|
||||
const STATUS_DISPLAY_BY_EXE_TAB = {
|
||||
1: { text: '已校对', type: 'success' },
|
||||
6: { text: '已执行', type: 'success' },
|
||||
5: { text: '不执行', type: 'warning' },
|
||||
9: { text: '取消执行', type: 'info' },
|
||||
};
|
||||
|
||||
const LEGACY_STATUS_TEXT = {
|
||||
待发送: '待签发',
|
||||
已发送: '已签发',
|
||||
'已发送/待执行': '已签发',
|
||||
已完成: '已校对',
|
||||
待配药: '已提交',
|
||||
已汇总: '已提交',
|
||||
已发放: '已发药',
|
||||
};
|
||||
|
||||
function getStatusDisplayText(row) {
|
||||
const dispenseCode = Number(row?.dispenseStatus);
|
||||
if (DISPENSE_STATUS_DISPLAY[dispenseCode]) {
|
||||
return DISPENSE_STATUS_DISPLAY[dispenseCode];
|
||||
}
|
||||
const tabText = STATUS_DISPLAY_BY_EXE_TAB[props.exeStatus]?.text;
|
||||
if (tabText) {
|
||||
return tabText;
|
||||
}
|
||||
const requestCode = Number(row?.requestStatus);
|
||||
if (REQUEST_STATUS_DISPLAY[requestCode]) {
|
||||
return REQUEST_STATUS_DISPLAY[requestCode];
|
||||
}
|
||||
return (
|
||||
LEGACY_STATUS_TEXT[row?.requestStatus_enumText] ||
|
||||
LEGACY_STATUS_TEXT[row?.dispenseStatus_enumText] ||
|
||||
row?.requestStatus_enumText ||
|
||||
row?.dispenseStatus_enumText ||
|
||||
'-'
|
||||
);
|
||||
}
|
||||
|
||||
function getStatusType(row) {
|
||||
const tabType = STATUS_DISPLAY_BY_EXE_TAB[props.exeStatus]?.type;
|
||||
if (tabType) {
|
||||
return tabType;
|
||||
}
|
||||
const status = row?.requestStatus;
|
||||
const map = {
|
||||
1: 'info',
|
||||
2: 'primary',
|
||||
3: 'success',
|
||||
4: 'warning',
|
||||
5: 'danger',
|
||||
6: 'danger',
|
||||
7: 'warning',
|
||||
};
|
||||
return map[status] || 'info';
|
||||
}
|
||||
|
||||
const activeNames = ref([]);
|
||||
const prescriptionList = ref([]);
|
||||
const deadline = ref(formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59');
|
||||
|
||||
@@ -89,7 +89,7 @@ function handleClick(tabName) {
|
||||
// 执行状态待执行
|
||||
exeStatus.value = 1;
|
||||
// 请求状态已校对
|
||||
requestStatus.value = RequestStatus.COMPLETED;
|
||||
requestStatus.value = 3;
|
||||
break;
|
||||
case 'completed':
|
||||
exeStatus.value = 6;
|
||||
|
||||
@@ -142,10 +142,10 @@
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="医嘱状态" prop="requestStatus_enumText" width="100" align="center">
|
||||
<el-table-column label="医嘱状态" prop="requestStatus_enumText" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusType(scope.row.requestStatus)" size="small">
|
||||
{{ getStatusDisplayText(scope.row) }}
|
||||
{{ scope.row.requestStatus_enumText }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -163,7 +163,6 @@ import {ref, computed, getCurrentInstance} from 'vue';
|
||||
import {adviceVerify, cancel, getPrescriptionList} from './api';
|
||||
import {patientInfoList} from '../../components/store/patient.js';
|
||||
import {formatDateStr} from '@/utils/index';
|
||||
import {RequestStatus} from '@/utils/medicalConstants';
|
||||
|
||||
const activeNames = ref([]);
|
||||
const prescriptionList = ref([]);
|
||||
@@ -174,34 +173,18 @@ const loading = ref(false);
|
||||
const chooseAll = ref(false);
|
||||
const selectionTrigger = ref(0);
|
||||
|
||||
/** 各页签对应的医嘱状态展示文案(与后端枚举值解耦,贴合校对业务语义) */
|
||||
const STATUS_DISPLAY_BY_TAB = {
|
||||
[RequestStatus.ACTIVE]: { text: '已签发', type: 'primary' },
|
||||
[RequestStatus.COMPLETED]: { text: '已校对', type: 'success' },
|
||||
[RequestStatus.DRAFT]: { text: '待签发', type: 'info' },
|
||||
[RequestStatus.STOPPED]: { text: '已停止', type: 'danger' },
|
||||
};
|
||||
|
||||
const getStatusType = (status) => {
|
||||
const tabType = STATUS_DISPLAY_BY_TAB[props.requestStatus]?.type;
|
||||
if (tabType) return tabType;
|
||||
const map = {
|
||||
1: 'info',
|
||||
2: 'primary',
|
||||
3: 'success',
|
||||
4: 'warning',
|
||||
5: 'danger',
|
||||
6: 'danger',
|
||||
7: 'info',
|
||||
};
|
||||
return map[status] || 'info';
|
||||
};
|
||||
|
||||
const getStatusDisplayText = (row) => {
|
||||
const tabText = STATUS_DISPLAY_BY_TAB[props.requestStatus]?.text;
|
||||
if (tabText) return tabText;
|
||||
return row.requestStatus_enumText || '';
|
||||
};
|
||||
1: 'info', // 待发送
|
||||
2: 'primary', // 已发送
|
||||
3: 'success', // 已完成
|
||||
4: 'warning', // 暂停
|
||||
5: 'danger', // 取消/待退
|
||||
6: 'danger', // 停嘱
|
||||
7: 'info' // 不执行
|
||||
}
|
||||
return map[status] || 'info'
|
||||
}
|
||||
const hasDispensedSelected = computed(() => {
|
||||
selectionTrigger.value;
|
||||
return getSelectRows().some(item => item.dispenseStatus === 4);
|
||||
@@ -211,10 +194,6 @@ const props = defineProps({
|
||||
type: Number,
|
||||
default: 2,
|
||||
},
|
||||
activeTab: {
|
||||
type: String,
|
||||
default: 'unverified',
|
||||
},
|
||||
});
|
||||
|
||||
function handleRadioChange() {
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
<!-- 使用模板引用 -->
|
||||
<PrescriptionList
|
||||
:requestStatus="requestStatus"
|
||||
:activeTab="activeName"
|
||||
:ref="(el) => setPrescriptionRef(el, tab.name)"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
@@ -83,16 +82,16 @@ function handleTabClick(tabName) {
|
||||
|
||||
switch (activeTabName) {
|
||||
case 'unverified':
|
||||
requestStatus.value = RequestStatus.ACTIVE;
|
||||
requestStatus.value = 2;
|
||||
break;
|
||||
case 'verified':
|
||||
requestStatus.value = RequestStatus.COMPLETED;
|
||||
requestStatus.value = 3;
|
||||
break;
|
||||
case 'stopped':
|
||||
requestStatus.value = RequestStatus.STOPPED;
|
||||
requestStatus.value = 6;
|
||||
break;
|
||||
case 'cancelled':
|
||||
requestStatus.value = RequestStatus.DRAFT;
|
||||
requestStatus.value = 1;
|
||||
break;
|
||||
}
|
||||
// 调用子组件方法
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div busNo="app-container">
|
||||
<el-form
|
||||
style="margin-top: 20px; margin-left: 20px"
|
||||
:model="queryParams"
|
||||
@@ -97,7 +97,7 @@
|
||||
|
||||
<el-row
|
||||
:gutter="10"
|
||||
class="mb8"
|
||||
busNo="mb8"
|
||||
style="margin-left: 20px; margin-right: 0px; margin-bottom: 5px"
|
||||
>
|
||||
<el-col :span="1.5">
|
||||
@@ -268,7 +268,7 @@
|
||||
/>
|
||||
<el-row
|
||||
:gutter="10"
|
||||
class="mb8"
|
||||
busNo="mb8"
|
||||
style="
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
@@ -298,7 +298,6 @@ const { proxy } = getCurrentInstance();
|
||||
const totalAmount = ref(0);
|
||||
const purchaseinventoryListAll = ref([]);
|
||||
const xiaojiTotal = ref([]);
|
||||
const rowSpanMap = ref({});
|
||||
const rowSpan = ref(1);
|
||||
const issuerOptions = ref([]);
|
||||
const payeeOptions = ref([]);
|
||||
@@ -549,92 +548,93 @@ function handleTotalAmount() {
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// 表格合并行方法(纯函数,不修改数据)
|
||||
// 门诊号合并行处理
|
||||
function getTotals(row, i) {
|
||||
let totalPriceSums = Number(purchaseinventoryList.value[i].totalPrice);
|
||||
|
||||
for (let j = 1; i - j >= 0; j++) {
|
||||
if (purchaseinventoryList.value[i].busNo == purchaseinventoryList.value[i - j].busNo) {
|
||||
totalPriceSums += Number(purchaseinventoryList.value[i - j].totalPrice);
|
||||
}
|
||||
}
|
||||
|
||||
xiaojiTotal.value.push({
|
||||
inde: i + 1,
|
||||
busNo: row.busNo,
|
||||
genderEnum_enumText: row.genderEnum_enumText,
|
||||
totalPrice: totalPriceSums.toFixed(4) || 0.0,
|
||||
});
|
||||
|
||||
purchaseinventoryList.value.splice(i + 1, 0, {
|
||||
busNo: row.busNo,
|
||||
genderEnum_enumText: row.genderEnum_enumText,
|
||||
departmentName: '小计',
|
||||
totalPrice: totalPriceSums.toFixed(4) || 0.0,
|
||||
});
|
||||
}
|
||||
|
||||
// 表格合并行方法
|
||||
const arraySpanMethod = ({ row, column, rowIndex, columnIndex }) => {
|
||||
// 合并门诊号列(columnIndex === 1)
|
||||
if (columnIndex === 1) {
|
||||
const spanInfo = rowSpanMap.value[rowIndex];
|
||||
if (spanInfo) {
|
||||
return { rowspan: spanInfo.rowspan, colspan: spanInfo.colspan || 1 };
|
||||
if (columnIndex === 1 && purchaseinventoryList.value.length > 0) {
|
||||
if (
|
||||
rowIndex === 0 ||
|
||||
(rowIndex > 0 && row.busNo !== purchaseinventoryList.value[rowIndex - 1]?.busNo)
|
||||
) {
|
||||
let rowspan = 1;
|
||||
let totalPriceSum = 0;
|
||||
|
||||
for (let i = rowIndex + 1; i < purchaseinventoryList.value.length + 1; i++) {
|
||||
if (purchaseinventoryList.value[i - 1].departmentName != '合计') {
|
||||
if (
|
||||
purchaseinventoryList.value[i] &&
|
||||
purchaseinventoryList.value[i].busNo === row.busNo
|
||||
) {
|
||||
rowspan++;
|
||||
totalPriceSum += Number(purchaseinventoryList.value[i].totalPrice);
|
||||
|
||||
if (i == purchaseinventoryList.value.length - 1) {
|
||||
let findIndexTotal = xiaojiTotal.value.findIndex((k) => k.busNo == row.busNo);
|
||||
if (findIndexTotal < 0) {
|
||||
getTotals(row, i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
totalPriceSum += Number(row.totalPrice);
|
||||
let findIndexTotal = xiaojiTotal.value.findIndex((k) => k.busNo == row.busNo);
|
||||
|
||||
if (findIndexTotal < 0) {
|
||||
xiaojiTotal.value.push({
|
||||
inde: i,
|
||||
genderEnum_enumText: row.genderEnum_enumText,
|
||||
busNo: row.busNo,
|
||||
totalPrice: totalPriceSum,
|
||||
});
|
||||
|
||||
purchaseinventoryList.value.splice(i, 0, {
|
||||
busNo: row.busNo,
|
||||
genderEnum_enumText: row.genderEnum_enumText,
|
||||
departmentName: '小计',
|
||||
totalPrice: totalPriceSum.toFixed(4) || 0.0,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
rowspan++;
|
||||
}
|
||||
|
||||
return { rowspan, colspan: 1 };
|
||||
} else {
|
||||
return { rowspan: 0, colspan: 0 };
|
||||
}
|
||||
}
|
||||
// 其他列不合并
|
||||
return { rowspan: 1, colspan: 1 };
|
||||
};
|
||||
|
||||
// 预处理列表数据:插入小计行、计算合并行信息
|
||||
// 此函数替代了原来在 arraySpanMethod 中 splice 修改数据的做法
|
||||
function processListWithSubtotals(list) {
|
||||
rowSpanMap.value = {};
|
||||
xiaojiTotal.value = [];
|
||||
|
||||
const result = [];
|
||||
let i = 0;
|
||||
|
||||
while (i < list.length) {
|
||||
const row = list[i];
|
||||
// 跳过已有的合计行
|
||||
if (row.departmentName === '合计') {
|
||||
result.push(row);
|
||||
rowSpanMap.value[result.length - 1] = { rowspan: 1, colspan: 1 };
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
const currentBusNo = row.busNo;
|
||||
let rowspan = 0;
|
||||
let totalPriceSum = 0;
|
||||
let j = i;
|
||||
|
||||
// 计算相同门诊号的行数
|
||||
while (j < list.length && list[j].busNo === currentBusNo && list[j].departmentName !== '合计') {
|
||||
rowspan++;
|
||||
totalPriceSum += Number(list[j].totalPrice) || 0;
|
||||
j++;
|
||||
}
|
||||
|
||||
// 设置第一行的 rowspan
|
||||
const startRow = result.length;
|
||||
rowSpanMap.value[startRow] = { rowspan, colspan: 1 };
|
||||
|
||||
// 添加数据行
|
||||
for (let k = i; k < j; k++) {
|
||||
result.push(list[k]);
|
||||
}
|
||||
|
||||
// 添加小计行(多于1行时才添加)
|
||||
if (rowspan > 1) {
|
||||
const subtotalRow = {
|
||||
...list[i],
|
||||
departmentName: '小计',
|
||||
totalPrice: totalPriceSum.toFixed(4),
|
||||
};
|
||||
// 小计行不合并
|
||||
rowSpanMap.value[result.length] = { rowspan: 1, colspan: 1 };
|
||||
result.push(subtotalRow);
|
||||
xiaojiTotal.value.push({
|
||||
inde: result.length,
|
||||
busNo: currentBusNo,
|
||||
genderEnum_enumText: list[i].genderEnum_enumText,
|
||||
totalPrice: totalPriceSum.toFixed(4),
|
||||
});
|
||||
}
|
||||
|
||||
i = j;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// 统计类型变化处理
|
||||
function inventoryChange(val) {
|
||||
queryParams.value.statisticsType = val;
|
||||
xiaojiTotal.value = [];
|
||||
purchaseinventoryList.value = [];
|
||||
rowSpanMap.value = {};
|
||||
getList();
|
||||
}
|
||||
|
||||
@@ -694,11 +694,8 @@ function getList(type) {
|
||||
: '0.0000' + (k.quantityUnit_dictText ? k.quantityUnit_dictText : '');
|
||||
});
|
||||
|
||||
// 处理搜索关键词或单页数据
|
||||
if (queryParams.value.searchKey || (total.value && total.value <= queryParams.value.pageSize)) {
|
||||
// 先处理小计行和合并信息
|
||||
purchaseinventoryList.value = processListWithSubtotals(purchaseinventoryList.value);
|
||||
|
||||
// 处理搜索关键词时的合计
|
||||
if (queryParams.value.searchKey) {
|
||||
purchaseinventoryList.value.forEach((k) => {
|
||||
if (k.departmentName !== '小计' && k.departmentName !== '合计') {
|
||||
totalPrice2 += Number(k.totalPrice) || 0;
|
||||
@@ -706,24 +703,39 @@ function getList(type) {
|
||||
});
|
||||
|
||||
totalPrice2 = totalPrice2 ? totalPrice2.toFixed(4) : totalPrice2;
|
||||
purchaseinventoryList.value.push({ departmentName: '合计', totalPrice: totalPrice2 });
|
||||
loading.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理分页数据
|
||||
purchaseinventoryList.value.forEach((k) => {
|
||||
if (total.value && total.value <= queryParams.value.pageSize) {
|
||||
totalPrice2 += Number(k.totalPrice);
|
||||
}
|
||||
});
|
||||
|
||||
if (total.value <= res.data.size) {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 单页数据合计
|
||||
if (total.value && total.value <= queryParams.value.pageSize) {
|
||||
totalPrice2 = totalPrice2 ? totalPrice2.toFixed(4) : totalPrice2;
|
||||
let pageNoAll = total.value / queryParams.value.pageSize;
|
||||
|
||||
if (Math.ceil(pageNoAll) == queryParams.value.pageNo) {
|
||||
purchaseinventoryList.value.push({ departmentName: '合计', totalPrice: totalPrice2 });
|
||||
rowSpanMap.value[purchaseinventoryList.value.length - 1] = { rowspan: 1, colspan: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
} else if (total.value && total.value > queryParams.value.pageSize && !queryParams.value.searchKey) {
|
||||
// 多页数据:先处理当前页数据确保rowSpanMap正确初始化,避免表格格式错乱
|
||||
purchaseinventoryList.value = processListWithSubtotals(purchaseinventoryList.value);
|
||||
loading.value = false;
|
||||
|
||||
// 然后获取全部数据进行完整处理
|
||||
// 多页数据处理
|
||||
if (total.value && total.value > queryParams.value.pageSize && !queryParams.value.searchKey) {
|
||||
let queryParamsValue = { ...queryParams.value };
|
||||
queryParamsValue.pageSize = total.value;
|
||||
queryParamsValue.pageNo = 1;
|
||||
|
||||
// 移除空值参数
|
||||
Object.keys(queryParamsValue).forEach(key => {
|
||||
if (queryParamsValue[key] === undefined || queryParamsValue[key] === null || queryParamsValue[key] === '') {
|
||||
delete queryParamsValue[key];
|
||||
@@ -734,7 +746,7 @@ function getList(type) {
|
||||
purchaseinventoryListAll.value = res.data.records || [];
|
||||
|
||||
if (purchaseinventoryListAll.value.length > 0) {
|
||||
purchaseinventoryListAll.value.map((k) => {
|
||||
purchaseinventoryListAll.value.map((k, index) => {
|
||||
k.totalPrice = k.totalPrice ? k.totalPrice.toFixed(4) : '0.0000';
|
||||
k.price = k.price ? k.price.toFixed(4) : '0.0000';
|
||||
k.number = k.number
|
||||
@@ -742,16 +754,45 @@ function getList(type) {
|
||||
: '0.0000' + (k.quantityUnit_dictText ? k.quantityUnit_dictText : '');
|
||||
|
||||
totalPrice2 += Number(k.totalPrice);
|
||||
|
||||
// 处理跨页门诊号小计
|
||||
for (let m = 1; m < index; m++) {
|
||||
if (
|
||||
queryParams.value.pageNo > 1 &&
|
||||
index == queryParams.value.pageSize * (queryParams.value.pageNo - 1) &&
|
||||
k.busNo == purchaseinventoryListAll.value[index - m]?.busNo
|
||||
) {
|
||||
let dispenseNoIndex1 = purchaseinventoryList.value.findIndex(
|
||||
(o) => o.departmentName == '小计' && o.busNo == purchaseinventoryListAll.value[index - m].busNo
|
||||
);
|
||||
|
||||
if (dispenseNoIndex1 > 0) {
|
||||
purchaseinventoryList.value[dispenseNoIndex1].totalPrice =
|
||||
(Number(purchaseinventoryList.value[dispenseNoIndex1].totalPrice) +
|
||||
Number(purchaseinventoryListAll.value[index - m].totalPrice)).toFixed(4) || '0.0000';
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
index + m == queryParams.value.pageSize * queryParams.value.pageNo &&
|
||||
k.busNo == purchaseinventoryListAll.value[index + m]?.busNo
|
||||
) {
|
||||
if (
|
||||
purchaseinventoryList.value[purchaseinventoryList.value.length - 1]
|
||||
.departmentName == '小计'
|
||||
) {
|
||||
purchaseinventoryList.value.splice(purchaseinventoryList.value.length - 1, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
purchaseinventoryList.value = processListWithSubtotals(purchaseinventoryListAll.value);
|
||||
|
||||
totalPrice2 = totalPrice2 ? totalPrice2.toFixed(4) : totalPrice2;
|
||||
loading.value = false;
|
||||
|
||||
let pageNoAll = total.value / queryParams.value.pageSize;
|
||||
if (Math.ceil(pageNoAll) == queryParams.value.pageNo) {
|
||||
purchaseinventoryList.value.push({ departmentName: '合计', totalPrice: totalPrice2 });
|
||||
rowSpanMap.value[purchaseinventoryList.value.length - 1] = { rowspan: 1, colspan: 1 };
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -740,59 +740,58 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 结果表格卡片 -->
|
||||
<el-card shadow="never" class="apply-card">
|
||||
<el-table
|
||||
ref="applyTableRef"
|
||||
v-loading="applyLoading"
|
||||
:data="applyList"
|
||||
row-key="surgeryNo"
|
||||
@row-click="handleApplyRowClick"
|
||||
:row-class-name="tableRowClassName"
|
||||
style="width: 100%"
|
||||
max-height="320"
|
||||
>
|
||||
<el-table-column type="selection" width="55" :selectable="handleSelectable" />
|
||||
<el-table-column label="ID" align="center" width="80" fixed>
|
||||
<template #default="{ $index }">
|
||||
{{ (applyQueryParams.pageNo - 1) * applyQueryParams.pageSize + $index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="姓名" align="center" prop="name" width="100" />
|
||||
<el-table-column label="手术单号" align="center" prop="surgeryNo" width="120" />
|
||||
<el-table-column label="手术名称" align="center" prop="descJson.surgeryName" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="申请科室" align="center" width="100" prop="applyDeptName" />
|
||||
<el-table-column label="手术类型" align="center" width="90">
|
||||
<template #default="scope">
|
||||
{{ getSurgeryTypeName(scope.row.surgeryType) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="手术等级" align="center" width="90">
|
||||
<template #default="scope">
|
||||
{{ getSurgeryLevelName(scope.row.surgeryLevel || scope.row.descJson?.surgeryLevel) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="麻醉方式" align="center" width="90">
|
||||
<template #default="scope">
|
||||
{{ getAnesthesiaName(scope.row.anesthesiaTypeEnum) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="主刀医生" align="center" width="100" prop="mainSurgeonName" />
|
||||
</el-table>
|
||||
<!-- 分页在卡片内部 -->
|
||||
<div class="apply-pagination">
|
||||
<pagination
|
||||
v-show="applyTotal > 0"
|
||||
:total="applyTotal"
|
||||
:page="applyQueryParams.pageNo"
|
||||
:limit="applyQueryParams.pageSize"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
@update:page="val => applyQueryParams.pageNo = val"
|
||||
@update:limit="val => applyQueryParams.pageSize = val"
|
||||
@pagination="getSurgicalScheduleList"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
<!-- 结果表格区 -->
|
||||
<el-table
|
||||
ref="applyTableRef"
|
||||
v-loading="applyLoading"
|
||||
:data="applyList"
|
||||
row-key="surgeryNo"
|
||||
@row-click="handleApplyRowClick"
|
||||
:row-class-name="tableRowClassName"
|
||||
style="width: 100%"
|
||||
max-height="340"
|
||||
:scroll="{ y: 340 }"
|
||||
>
|
||||
<el-table-column type="selection" width="55" :selectable="handleSelectable" />
|
||||
<el-table-column label="ID" align="center" width="80" fixed>
|
||||
<template #default="{ $index }">
|
||||
{{ (applyQueryParams.pageNo - 1) * applyQueryParams.pageSize + $index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="姓名" align="center" prop="name" width="100" />
|
||||
<el-table-column label="手术单号" align="center" prop="surgeryNo" width="120" />
|
||||
<el-table-column label="手术名称" align="center" prop="descJson.surgeryName" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="申请科室" align="center" width="100" prop="applyDeptName" />
|
||||
<el-table-column label="手术类型" align="center" width="90">
|
||||
<template #default="scope">
|
||||
{{ getSurgeryTypeName(scope.row.surgeryType) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="手术等级" align="center" width="90">
|
||||
<template #default="scope">
|
||||
{{ getSurgeryLevelName(scope.row.surgeryLevel || scope.row.descJson?.surgeryLevel) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="麻醉方式" align="center" width="90">
|
||||
<template #default="scope">
|
||||
{{ getAnesthesiaName(scope.row.anesthesiaTypeEnum) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="主刀医生" align="center" width="100" prop="mainSurgeonName" />
|
||||
</el-table>
|
||||
|
||||
<!-- 底部分页区 -->
|
||||
<div class="pagination-container apply-pagination">
|
||||
<pagination
|
||||
v-show="applyTotal > 0"
|
||||
:total="applyTotal"
|
||||
:page="applyQueryParams.pageNo"
|
||||
:limit="applyQueryParams.pageSize"
|
||||
@update:page="val => applyQueryParams.pageNo = val"
|
||||
@update:limit="val => applyQueryParams.pageSize = val"
|
||||
@pagination="getSurgicalScheduleList"
|
||||
/>
|
||||
</div>
|
||||
<!-- 底部操作区 -->
|
||||
<template #footer>
|
||||
<div class="dialog-footer" style="padding-top: 12px; border-top: 1px solid #ebeef5">
|
||||
@@ -2340,35 +2339,19 @@ function getRowClassName({ row, rowIndex }) {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/* 手术申请查询弹窗 — flex 布局确保分页不溢出 */
|
||||
/* 手术申请查询弹窗 — 分页与footer间距 */
|
||||
.surgery-apply-dialog :deep(.el-dialog__body) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.surgery-apply-dialog :deep(.el-dialog__footer) {
|
||||
padding-top: 0;
|
||||
}
|
||||
.surgery-apply-dialog :deep(.apply-card) {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
}
|
||||
.surgery-apply-dialog :deep(.apply-card .el-card__body) {
|
||||
overflow-y: auto;
|
||||
padding-top: 8px;
|
||||
}
|
||||
.surgery-apply-dialog :deep(.apply-pagination) {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 8px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
}
|
||||
.surgery-apply-dialog :deep(.apply-pagination .pagination-container) {
|
||||
margin-top: 0;
|
||||
padding-top: 12px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.surgery-apply-dialog :deep(.apply-pagination .el-pagination) {
|
||||
position: static;
|
||||
margin-right: 80px;
|
||||
}
|
||||
|
||||
/* 选中行样式 */
|
||||
@@ -2384,33 +2367,17 @@ function getRowClassName({ row, rowIndex }) {
|
||||
|
||||
<style>
|
||||
/* 手术申请查询弹窗 — 非 scoped 确保穿透 teleport */
|
||||
.surgery-apply-dialog .el-dialog__body {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
padding-bottom: 16px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
.surgery-apply-dialog .el-dialog__footer {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
.surgery-apply-dialog .apply-card {
|
||||
flex: 1 !important;
|
||||
overflow: hidden !important;
|
||||
min-height: 0 !important;
|
||||
}
|
||||
.surgery-apply-dialog .apply-card .el-card__body {
|
||||
overflow-y: auto !important;
|
||||
}
|
||||
.surgery-apply-dialog .apply-pagination {
|
||||
display: flex !important;
|
||||
justify-content: flex-end !important;
|
||||
padding-top: 8px !important;
|
||||
border-top: 1px solid #ebeef5 !important;
|
||||
}
|
||||
.surgery-apply-dialog .apply-pagination .pagination-container {
|
||||
margin-top: 0 !important;
|
||||
padding-top: 12px !important;
|
||||
padding-bottom: 16px !important;
|
||||
}
|
||||
.surgery-apply-dialog .apply-pagination .el-pagination {
|
||||
position: static !important;
|
||||
margin-right: 80px !important;
|
||||
}
|
||||
.surgery-apply-dialog .el-dialog__body {
|
||||
padding-bottom: 16px !important;
|
||||
}
|
||||
.surgery-apply-dialog .el-dialog__footer {
|
||||
padding-top: 8px !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -90,14 +90,14 @@
|
||||
<div class="candidate-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="selectedCandidates.length === 0 || isQueryingHistory"
|
||||
:disabled="selectedCandidates.length === 0"
|
||||
@click="handleAddToQueue"
|
||||
>
|
||||
加入队列 >>
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="filteredCandidatePoolList.length === 0 || isQueryingHistory"
|
||||
:disabled="filteredCandidatePoolList.length === 0"
|
||||
@click="handleAddAllToQueue"
|
||||
>
|
||||
一键加入队列
|
||||
@@ -109,19 +109,6 @@
|
||||
<div class="right-panel">
|
||||
<div class="panel-header">
|
||||
<span class="panel-title">② 智能队列 (全科)</span>
|
||||
<div class="history-query">
|
||||
<el-date-picker
|
||||
v-model="queryDate"
|
||||
type="date"
|
||||
placeholder="选择日期"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
size="small"
|
||||
style="width: 150px"
|
||||
/>
|
||||
<el-button type="primary" size="small" @click="handleHistoryQuery">查询</el-button>
|
||||
<el-button size="small" @click="handleTodayQuery">今天</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<el-table
|
||||
@@ -188,7 +175,7 @@
|
||||
<div class="queue-actions-left">
|
||||
<el-button
|
||||
type="danger"
|
||||
:disabled="!selectedQueueRow || isQueryingHistory"
|
||||
:disabled="!selectedQueueRow"
|
||||
size="small"
|
||||
@click="handleRemoveFromQueue"
|
||||
>
|
||||
@@ -196,7 +183,7 @@
|
||||
</el-button>
|
||||
<el-button
|
||||
type="info"
|
||||
:disabled="!selectedQueueRow || !canMoveUp || isQueryingHistory"
|
||||
:disabled="!selectedQueueRow || !canMoveUp"
|
||||
size="small"
|
||||
@click="handleMoveUp"
|
||||
>
|
||||
@@ -204,7 +191,7 @@
|
||||
</el-button>
|
||||
<el-button
|
||||
type="info"
|
||||
:disabled="!selectedQueueRow || !canMoveDown || isQueryingHistory"
|
||||
:disabled="!selectedQueueRow || !canMoveDown"
|
||||
size="small"
|
||||
@click="handleMoveDown"
|
||||
>
|
||||
@@ -272,35 +259,30 @@
|
||||
<div class="control-buttons">
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="isQueryingHistory"
|
||||
@click="handleSelectCall"
|
||||
>
|
||||
选呼
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
:disabled="isQueryingHistory"
|
||||
@click="handleNextPatient"
|
||||
>
|
||||
下一患者
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
:disabled="isQueryingHistory"
|
||||
@click="handleSkip"
|
||||
>
|
||||
跳过
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:disabled="isQueryingHistory"
|
||||
@click="handleComplete"
|
||||
>
|
||||
完成
|
||||
</el-button>
|
||||
<el-button
|
||||
type="info"
|
||||
:disabled="isQueryingHistory"
|
||||
@click="handleRequeue"
|
||||
>
|
||||
过号重排
|
||||
@@ -700,14 +682,6 @@ const showOnlyWaiting = ref(false)
|
||||
// Bug #411:诊室过滤,替代原来的科室下拉框(selectedDept/departmentList 已移除)
|
||||
const selectedRoom = ref('all')
|
||||
|
||||
// 历史队列查询日期 (默认当天)
|
||||
const getTodayStr = () => {
|
||||
const now = new Date()
|
||||
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
|
||||
}
|
||||
const queryDate = ref(getTodayStr())
|
||||
const isQueryingHistory = computed(() => queryDate.value !== getTodayStr())
|
||||
|
||||
// 修复【#397】:动态获取当前科室名称
|
||||
const currentDeptName = computed(() => {
|
||||
return userStore.deptName || userStore.orgName || '心内科'
|
||||
@@ -927,12 +901,14 @@ const mapFrontendStatusToBackend = (status) => {
|
||||
}
|
||||
|
||||
// 从数据库加载队列
|
||||
const loadQueueFromDb = async (dateStr) => {
|
||||
const loadQueueFromDb = async () => {
|
||||
try {
|
||||
// Bug #411:不再按科室选筛加载,后端默认按当前登录人科室查询
|
||||
const organizationId = undefined
|
||||
const queryDateStr = dateStr || queryDate.value
|
||||
const res = await getTriageQueueList({ organizationId, date: queryDateStr }).catch((err) => {
|
||||
// 只查询今天的患者
|
||||
const today = new Date()
|
||||
const todayStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`
|
||||
const res = await getTriageQueueList({ organizationId, date: todayStr }).catch((err) => {
|
||||
console.error('【心内科】loadQueueFromDb 请求异常:', err)
|
||||
return { code: 500, msg: err?.message || '请求失败', data: null }
|
||||
})
|
||||
@@ -955,6 +931,10 @@ const loadQueueFromDb = async (dateStr) => {
|
||||
originalQueueList.value = list
|
||||
.map((it) => {
|
||||
const frontendStatus = mapBackendStatusToFrontend(it.status)
|
||||
// 调试日志:检查状态映射
|
||||
if (list.length <= 5) {
|
||||
console.log('【心内科】状态映射:后端状态=', it.status, '-> 前端状态=', frontendStatus, '患者=', it.patientName)
|
||||
}
|
||||
// 计算等待时间:基于创建时间(createTime)
|
||||
let waitingTime = '00:00'
|
||||
if (it.createTime) {
|
||||
@@ -992,6 +972,14 @@ const loadQueueFromDb = async (dateStr) => {
|
||||
organizationId: it.organizationId
|
||||
}
|
||||
})
|
||||
.filter((item) => {
|
||||
// 过滤掉"已完成"状态的患者,不显示在队列中
|
||||
if (item.status === '已完成') {
|
||||
console.log('【心内科】过滤掉已完成状态的患者:', item.patientName)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// 调试日志:检查查找结果
|
||||
const callingCount = originalQueueList.value.filter(i => i.status === '叫号中').length
|
||||
@@ -1208,6 +1196,9 @@ const formatSecondsToMmSs = (totalSeconds) => {
|
||||
const filteredQueueList = computed(() => {
|
||||
let filtered = originalQueueList.value
|
||||
|
||||
// 先过滤掉"已完成"状态的患者(无论什么情况都不显示)
|
||||
filtered = filtered.filter(item => item.status !== '已完成')
|
||||
|
||||
// 再按诊室过滤
|
||||
if (selectedRoom.value !== 'all') {
|
||||
filtered = filtered.filter(item => item.room === selectedRoom.value)
|
||||
@@ -1636,26 +1627,6 @@ const handleRefresh = async () => {
|
||||
ElMessage.success('已刷新(已从数据库恢复队列)')
|
||||
}
|
||||
|
||||
// 历史队列查询
|
||||
const handleHistoryQuery = async () => {
|
||||
if (!queryDate.value) {
|
||||
ElMessage.warning('请选择查询日期')
|
||||
return
|
||||
}
|
||||
console.log('【心内科】历史队列查询:', queryDate.value)
|
||||
await loadQueueFromDb(queryDate.value)
|
||||
if (isQueryingHistory.value) {
|
||||
ElMessage.success(`已加载 ${queryDate.value} 的队列数据`)
|
||||
}
|
||||
}
|
||||
|
||||
// 回到今天
|
||||
const handleTodayQuery = async () => {
|
||||
queryDate.value = getTodayStr()
|
||||
await loadQueueFromDb(getTodayStr())
|
||||
ElMessage.success('已切换到今天队列')
|
||||
}
|
||||
|
||||
// 退出
|
||||
const handleExit = () => {
|
||||
ElMessage.info('退出功能待实现')
|
||||
@@ -2194,21 +2165,12 @@ onUnmounted(() => {
|
||||
padding: 15px 20px;
|
||||
border-bottom: 2px solid #409eff;
|
||||
background-color: #f8f9fa;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.panel-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.history-query {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.table-container {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user