26 Commits

Author SHA1 Message Date
wangjian963
f3d011951b 78 增加门诊医生开立检验申请单--对搜索项目区实现懒加载,以及动态搜索。 2026-03-13 18:34:14 +08:00
7dc76d7b59 fix(advice): 修复禅道 Bug #147 - 添加耗材处理逻辑
- 在 saveRegAdvice 方法中添加耗材列表过滤
- 添加 handDevice 方法调用处理耗材请求
- 实现完整的 handDevice 方法处理耗材的保存、签发和删除操作
- 添加必要的导入:ActivityDefinition、DeviceRequest、IDeviceRequestService、IDeviceDispenseService
2026-03-13 12:10:24 +08:00
b2dec2667a fix(document): 修复文书定义树形列表查询逻辑
- 添加了对organizationId和useRanges参数的空值检查和日志警告
- 在SQL查询中增加了isValid字段过滤条件
- 添加了对primaryMenuEnum参数的条件查询支持
- 增加了详细的请求参数和查询结果日志记录
- 优化了参数传递的一致性,使用变量替代直接访问对象属性
2026-03-13 12:10:24 +08:00
HuangXinQuan
879d31b51d 165 药房管理-》门诊发药:字段内容显示问题 2026-03-13 11:20:23 +08:00
HuangXinQuan
473a5f7f06 149 门诊管理-》门诊输液查询不到患者已收费注射类的药品信息 2026-03-13 09:44:32 +08:00
2eec988c56 Merge remote-tracking branch 'origin/develop' into develop 2026-03-13 08:59:35 +08:00
8820048d55 feat(emr): 添加住院病历菜单类型枚举
- 新增 primaryMenuEnum 字段用于标识住院病历类型
- 设置默认值为 1 对应住院病历文档类型枚举
2026-03-13 08:59:10 +08:00
6af7720470 feat(diagnosis): 完善诊断模块功能并优化病历数据获取
- 添加isSaving状态控制保存过程
- 监听患者信息变化自动获取病历详情和诊断列表
- 增强getDetail方法添加错误处理和日志输出
- 重构handleAddDiagnosis方法分离验证逻辑到独立函数
- 优化病历详情获取接口同时查询门诊和住院病历数据
- 添加文档定义树形列表按使用范围筛选功能
- 修复历史病历数据加载错误处理机制
2026-03-12 23:21:34 +08:00
wangjian963
5f134945ab 1. 在ActivityDefinition实体类及相关DTO中添加inspectionTypeId字段
2. 新增检验类型分页查询接口及前端API调用
3. 优化检验申请模块的前后端交互逻辑
4.完成修改78 增加门诊医生开立检验申请单立检验申请单的检验项目写死的问题
5.对检验目录设置的查询,更新和保存进行修改完善。
6.对检验项目设置的页面使用vue3+elementui进行修改。
2026-03-12 18:58:54 +08:00
bc12cc1b08 fix(charge): 修复用法绑定耗材门诊收费未显示问题 (Bug #145) 2026-03-12 18:05:17 +08:00
17b8ea7192 fix(nurse-station): 修复住院护士站门户护理级别筛选功能失效问题 (Bug #172) 2026-03-12 17:38:31 +08:00
Ranyunqiao
2bfdd686c7 栈溢出 2026-03-12 17:31:50 +08:00
Ranyunqiao
066cfaba46 168 入科分配床位填写的住院医生、主治医生、责任护士字段的内容双击查看未显示。 2026-03-12 16:58:39 +08:00
e8850e85fc feat(transfer): add warehouse type support and lab specimen tables 2026-03-12 16:31:31 +08:00
d083a3123a fix: Bug #177 修复新增医嘱报错 - category_code 类型转换错误
问题原因:
SQL查询中尝试将 wor_activity_definition.category_code(中文值如'检验'、'检查')
直接转换为 INTEGER 类型,导致 PostgreSQL 类型转换错误。

修复方案:
使用 CASE WHEN 语句将中文 category_code 映射为对应的整数值:
- 检验 -> 1
- 检查 -> 2
- 护理 -> 3
- 手术 -> 4
- 其他 -> 5

这与 ActivityType 枚举定义保持一致。
2026-03-12 15:53:06 +08:00
96c1927f8d fix: Bug #177 门诊医生站耗材医嘱保存提示未匹配库存信息
问题原因:
1. 前端查询耗材列表时未设置 adviceTableName 字段
2. 后端库存校验时严格要求 adviceTableName 匹配,导致耗材无法匹配库存

修复方案:
1. 前端(adviceBaseList.vue): 添加 adviceTableName = 'adm_device_definition' 字段
2. 后端(AdviceUtils.java): 添加容错处理,当 adviceTableName 为空时跳过该项匹配

双保险策略确保问题彻底解决。
2026-03-12 15:44:03 +08:00
bdac9d0709 feat: 护理记录患者列表恢复按科室过滤 (Bug #175)
- 恢复orgId过滤,只显示当前用户科室的在院患者
- 配合后端SQL修复,从就诊表获取科室ID
- 李光明等同科室患者现在可以正常显示
2026-03-12 15:18:42 +08:00
8faba1ea21 fix: 护理记录患者列表科室ID从就诊表获取 (Bug #175)
- 将org_id来源从adm_patient改为adm_encounter
- adm_patient.organization_id通常为空
- adm_encounter.organization_id才是入院科室
- 修复按科室过滤时查不到患者的问题
2026-03-12 15:13:37 +08:00
dd9b77f6bb fix: 护理记录患者列表显示所有在院患者 (Bug #175)
- 移除orgId过滤,不再按科室限制患者显示
- 修复李光明等患者无法显示的问题
- 现在显示所有在院患者,不按科室/病区过滤
2026-03-12 14:57:27 +08:00
d45955f6de Merge branch 'develop' of https://gitea.gentronhealth.com/wangyizhe/his into develop 2026-03-12 14:56:13 +08:00
f905915f34 fix: 护理记录患者列表改为按病区过滤 (Bug #175)
- 将orgId过滤改为wardLocationId过滤
- 显示当前护士负责病区的所有患者
- 修复李光明等患者无法显示的问题
2026-03-12 14:55:58 +08:00
Ranyunqiao
52951d7296 167 住院管理-》住院护士站-》入出转管理:护士登录的科室能接收查看到其他科室的入科患者 入院病区字段下拉内容限制只能显示当前登录科室对应的病区,如该护士还有其他科室的权限需要做切换科室操作。 2026-03-12 14:30:19 +08:00
3c47979913 fix: 修复护理记录患者列表不显示在院患者的问题 (Bug #175)
- 将INNER JOIN改为LEFT JOIN,允许患者未分配床位时也能显示在列表中
- 修复getPatientPage和getNursingPatientPage两个查询
- 解决患者已入院但无床位信息时查询不到数据的问题
2026-03-12 14:15:56 +08:00
9aad809322 Merge remote-tracking branch 'origin/develop' into develop 2026-03-12 13:49:07 +08:00
b7850e5b8a fix: 修复耗材目录添加项目时地点字段缺少耗材库数据问题
- 问题:formList参数只包含'11,16'(药库和药房),缺少'17'(耗材库)
- 解决:将formList参数从'11,16'修改为'11,16,17'
- 影响:器材目录新增对话框的地点下拉选项现在可以显示耗材库数据

Closes #174
2026-03-12 13:47:36 +08:00
4277a369d2 fix(order): 解决医嘱类型字段处理问题
- 优化了JSON解析逻辑,避免重复解析contentJson字段
- 确保therapyEnum字段正确传递,默认值设置为长期医嘱('1')
- 修复了医嘱保存和签发过程中类型字段丢失的问题
- 统一了前后端therapyEnum字段的默认值处理逻辑
- 添加了必要的注释说明字段处理规则
2026-03-12 12:42:17 +08:00
65 changed files with 2531 additions and 1757 deletions

View File

@@ -464,17 +464,86 @@ public class CommonServiceImpl implements ICommonService {
*/
@Override
public List<LocationDto> getPractitionerWard() {
// 查询当前登录者管理的病区
// 获取当前登录用户信息
Long practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
Long currentOrgId = SecurityUtils.getLoginUser().getOrgId();
log.info("getPractitionerWard - practitionerId: {}, currentOrgId: {}", practitionerId, currentOrgId);
// 获取用户配置的位置 ID 列表(可能包含科室、病区等)
List<Long> locationIds = practitionerRoleService.getLocationIdsByPractitionerId(practitionerId);
List<Location> locationList
= locationService.getLocationList(locationIds, Collections.singletonList(LocationStatus.ACTIVE.getValue()));
// 获取用户配置的科室 ID 列表
List<Long> orgIds = practitionerRoleService.getOrgIdsByPractitionerId(practitionerId);
log.info("getPractitionerWard - locationIds: {}, orgIds: {}", locationIds, orgIds);
List<Location> wardList = new ArrayList<>();
for (Location ward : locationList) {
if (LocationForm.WARD.getValue().equals(ward.getFormEnum())) {
wardList.add(ward);
// 方式 1从 locationIds 中过滤出病区
if (locationIds != null && !locationIds.isEmpty()) {
// 过滤掉 null 值
locationIds = locationIds.stream().filter(id -> id != null).collect(java.util.stream.Collectors.toList());
if (!locationIds.isEmpty()) {
List<Location> locationList
= locationService.getLocationList(locationIds, Collections.singletonList(LocationStatus.ACTIVE.getValue()));
log.info("getPractitionerWard - 从 locationIds 查询到的位置总数:{}", locationList != null ? locationList.size() : 0);
for (Location location : locationList) {
log.info("getPractitionerWard - 位置id={}, name={}, formEnum={}, organizationId={}",
location.getId(), location.getName(), location.getFormEnum(), location.getOrganizationId());
if (LocationForm.WARD.getValue().equals(location.getFormEnum())) {
// 如果当前有选择科室,只添加当前科室的病区
if (currentOrgId == null || currentOrgId.equals(location.getOrganizationId())) {
wardList.add(location);
}
}
}
}
}
// 方式 2从 orgIds 查询病区(补充查询,确保能获取到病区)
if (orgIds != null && !orgIds.isEmpty()) {
// 过滤掉 null 值并去重
orgIds = orgIds.stream().filter(id -> id != null).distinct().collect(java.util.stream.Collectors.toList());
if (!orgIds.isEmpty()) {
log.info("getPractitionerWard - 从 orgIds 查询病区orgIds: {}", orgIds);
for (Long orgId : orgIds) {
// 如果当前有选择科室,只查询当前科室的病区
if (currentOrgId != null && !currentOrgId.equals(orgId)) {
continue;
}
List<Location> orgWards = locationService.getWardList(orgId);
log.info("getPractitionerWard - orgId: {} 查询到病区数:{}", orgId, orgWards != null ? orgWards.size() : 0);
if (orgWards != null && !orgWards.isEmpty()) {
wardList.addAll(orgWards);
}
}
}
}
// 方式 3如果仍然没有病区且 currentOrgId 为空,尝试获取所有病区
if (wardList.isEmpty() && currentOrgId == null) {
log.info("getPractitionerWard - 尝试获取所有病区");
List<Location> allWards = locationService.getWardList(null);
log.info("getPractitionerWard - 所有病区数:{}", allWards != null ? allWards.size() : 0);
if (allWards != null && !allWards.isEmpty()) {
wardList.addAll(allWards);
}
}
// 去重:根据病区 ID 去重(因为方式 1 和方式 2 可能会查询到相同的病区)
if (!wardList.isEmpty()) {
wardList = wardList.stream()
.collect(java.util.stream.Collectors.collectingAndThen(
java.util.stream.Collectors.toCollection(() ->
new java.util.TreeSet<>(java.util.Comparator.comparing(Location::getId))),
ArrayList::new
));
}
log.info("getPractitionerWard - 最终病区数:{}", wardList.size());
// 转换为 DTO
List<LocationDto> locationDtoList = new ArrayList<>();
LocationDto locationDto;
for (Location ward : wardList) {
@@ -482,6 +551,31 @@ public class CommonServiceImpl implements ICommonService {
BeanUtils.copyProperties(ward, locationDto);
locationDtoList.add(locationDto);
}
return locationDtoList;
}
/**
* 将Location列表转换为LocationDto列表
*
* @param locationList Location列表
* @return LocationDto列表
*/
private List<LocationDto> convertToLocationDtoList(List<Location> locationList) {
List<LocationDto> locationDtoList = new ArrayList<>();
if (locationList == null || locationList.isEmpty()) {
return locationDtoList;
}
LocationDto locationDto;
for (Location location : locationList) {
// 只返回病区类型的位置
if (LocationForm.WARD.getValue().equals(location.getFormEnum())) {
locationDto = new LocationDto();
BeanUtils.copyProperties(location, locationDto);
locationDtoList.add(locationDto);
}
}
return locationDtoList;
}

View File

@@ -202,8 +202,8 @@ public class CommonAppController {
* @return 病区列表
*/
@GetMapping(value = "/practitioner-ward")
public List<LocationDto> getPractitionerWard() {
return commonService.getPractitionerWard();
public R<?> getPractitionerWard() {
return R.ok(commonService.getPractitionerWard());
}
/**

View File

@@ -193,6 +193,13 @@ public class DiagTreatMAppServiceImpl implements IDiagTreatMAppService {
DiagnosisTreatmentSelParam.setYbType(null); // 临时移除防止HisQueryUtils处理
}
// 临时保存inspectionTypeId值手动添加带表别名的条件
Long inspectionTypeIdValue = null;
if (DiagnosisTreatmentSelParam != null && DiagnosisTreatmentSelParam.getInspectionTypeId() != null) {
inspectionTypeIdValue = DiagnosisTreatmentSelParam.getInspectionTypeId();
DiagnosisTreatmentSelParam.setInspectionTypeId(null); // 临时移除防止HisQueryUtils处理
}
// 构建查询条件
QueryWrapper<DiagnosisTreatmentDto> queryWrapper = HisQueryUtils.buildQueryWrapper(DiagnosisTreatmentSelParam,
searchKey, new HashSet<>(Arrays.asList("bus_no", "name", "py_str", "wb_str")), request);
@@ -204,6 +211,13 @@ public class DiagTreatMAppServiceImpl implements IDiagTreatMAppService {
DiagnosisTreatmentSelParam.setYbType(ybTypeValue);
}
// 如果需要按检验类型过滤,添加带表别名的条件
if (inspectionTypeIdValue != null) {
queryWrapper.eq("T1.inspection_type_id", inspectionTypeIdValue);
// 恢复参数对象中的值
DiagnosisTreatmentSelParam.setInspectionTypeId(inspectionTypeIdValue);
}
// 分页查询
IPage<DiagnosisTreatmentDto> diseaseTreatmentPage
= activityDefinitionManageMapper.getDiseaseTreatmentPage(new Page<DiagnosisTreatmentDto>(pageNo, pageSize), queryWrapper);
@@ -336,6 +350,8 @@ public class DiagTreatMAppServiceImpl implements IDiagTreatMAppService {
// 显式设置新增的字段
activityDefinition.setSortOrder(diagnosisTreatmentUpDto.getSortOrder());
activityDefinition.setServiceRange(diagnosisTreatmentUpDto.getServiceRange());
// 显式设置检验类型ID
activityDefinition.setInspectionTypeId(diagnosisTreatmentUpDto.getInspectionTypeId());
// 显式设置划价标记(避免前端字段/类型差异导致 copyProperties 后仍为默认值)
activityDefinition.setPricingFlag(diagnosisTreatmentUpDto.getPricingFlag());
@@ -479,11 +495,13 @@ public class DiagTreatMAppServiceImpl implements IDiagTreatMAppService {
ActivityDefinition activityDefinition = new ActivityDefinition();
BeanUtils.copyProperties(diagnosisTreatmentUpDto, activityDefinition);
// 显式设置新增的字段
activityDefinition.setSortOrder(diagnosisTreatmentUpDto.getSortOrder());
activityDefinition.setServiceRange(diagnosisTreatmentUpDto.getServiceRange());
// 显式设置检验类型ID
activityDefinition.setInspectionTypeId(diagnosisTreatmentUpDto.getInspectionTypeId());
// 如果前端没有传入编码则使用10位数基础采番
if (StringUtils.isEmpty(activityDefinition.getBusNo())) {
String code = assignSeqUtil.getSeq(AssignSeqEnum.ACTIVITY_DEFINITION_NUM.getPrefix(), 10);

View File

@@ -1,6 +1,9 @@
package com.openhis.web.datadictionary.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.core.common.core.domain.R;
import com.openhis.lab.domain.InspectionType;
import com.openhis.lab.service.IInspectionTypeService;
import com.openhis.web.datadictionary.appservice.IDiagTreatMAppService;
import com.openhis.web.datadictionary.dto.DiagnosisTreatmentSelParam;
import com.openhis.web.datadictionary.dto.DiagnosisTreatmentUpDto;
@@ -30,6 +33,9 @@ public class DiagnosisTreatmentController {
@Resource
private IDiagTreatMAppService diagTreatMAppService;
@Resource
private IInspectionTypeService inspectionTypeService;
/**
* 诊疗目录初期查询
*
@@ -188,4 +194,17 @@ public class DiagnosisTreatmentController {
public R<?> updatePricingFlag(@RequestBody List<Long> ids, @RequestParam Integer pricingFlag) {
return diagTreatMAppService.updatePricingFlag(ids, pricingFlag);
}
/**
* 获取检验类型列表(用于下拉框)
*
* @return 检验类型列表
*/
@GetMapping("/inspection-types")
public R<?> getInspectionTypes() {
LambdaQueryWrapper<InspectionType> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(InspectionType::getValidFlag, 1)
.orderByAsc(InspectionType::getSortOrder);
return R.ok(inspectionTypeService.list(queryWrapper));
}
}

View File

@@ -130,4 +130,13 @@ public class DiagnosisTreatmentDto {
/** 服务范围 */
private String serviceRange;
/** 检验类型ID */
@Dict(dictTable = "inspection_type", dictCode = "id", dictText = "name", deleteFlag = "valid_flag")
@JsonSerialize(using = ToStringSerializer.class)
private Long inspectionTypeId;
private String inspectionTypeId_dictText;
/** 检验类型名称(用于前端 testType 字段) */
private String testType;
}

View File

@@ -30,4 +30,7 @@ public class DiagnosisTreatmentSelParam {
/** 状态 */
private Integer statusEnum;
/** 检验类型ID */
private Long inspectionTypeId;
}

View File

@@ -128,4 +128,8 @@ public class DiagnosisTreatmentUpDto {
/** 服务范围 */
private String serviceRange;
/** 检验类型ID */
@JsonSerialize(using = ToStringSerializer.class)
private Long inspectionTypeId;
}

View File

@@ -353,8 +353,6 @@ public class DoctorStationDiagnosisAppServiceImpl implements IDoctorStationDiagn
encounterDiagnosis.setLongTermFlag(saveDiagnosisChildParam.getLongTermFlag());
encounterDiagnosis.setOnsetDate(saveDiagnosisChildParam.getOnsetDate());
encounterDiagnosis.setDiagnosisTime(saveDiagnosisChildParam.getDiagnosisTime());
encounterDiagnosis.setOnsetDate(saveDiagnosisChildParam.getOnsetDate());
encounterDiagnosis.setDiagnosisTime(saveDiagnosisChildParam.getDiagnosisTime());
if(encounterDiagnosis.getCreateBy() == null){
encounterDiagnosis.setCreateBy(username);
}
@@ -383,8 +381,6 @@ public class DoctorStationDiagnosisAppServiceImpl implements IDoctorStationDiagn
encounterDiagnosis.setLongTermFlag(saveDiagnosisChildParam.getLongTermFlag());
encounterDiagnosis.setOnsetDate(saveDiagnosisChildParam.getOnsetDate());
encounterDiagnosis.setDiagnosisTime(saveDiagnosisChildParam.getDiagnosisTime());
encounterDiagnosis.setOnsetDate(saveDiagnosisChildParam.getOnsetDate());
encounterDiagnosis.setDiagnosisTime(saveDiagnosisChildParam.getDiagnosisTime());
if(encounterDiagnosis.getCreateBy() == null){
encounterDiagnosis.setCreateBy(username);
}

View File

@@ -16,10 +16,12 @@ import java.util.Date;
import java.sql.Timestamp;
import com.openhis.common.enums.BindingType;
import com.openhis.common.enums.EncounterStatus;
import com.openhis.document.domain.DocRecord;
import com.openhis.document.domain.Emr;
import com.openhis.document.domain.EmrDetail;
import com.openhis.document.domain.EmrDict;
import com.openhis.document.domain.EmrTemplate;
import com.openhis.document.service.IDocRecordService;
import com.openhis.document.service.IEmrDetailService;
import com.openhis.document.service.IEmrDictService;
import com.openhis.document.service.IEmrService;
@@ -54,6 +56,9 @@ public class DoctorStationEmrAppServiceImpl implements IDoctorStationEmrAppServi
@Resource
IEmrDictService emrDictService;
@Resource
IDocRecordService docRecordService;
@Resource
private EncounterMapper encounterMapper;
@@ -128,14 +133,33 @@ public class DoctorStationEmrAppServiceImpl implements IDoctorStationEmrAppServi
/**
* 获取病历详情
* 同时检查门诊病历(emr表)和住院病历(doc_record表)
*
* @param encounterId 就诊id
* @return 病历详情
*/
@Override
public R<?> getEmrDetail(Long encounterId) {
// 先查询门诊病历(emr表)
Emr emrDetail = emrService.getOne(new LambdaQueryWrapper<Emr>().eq(Emr::getEncounterId, encounterId));
return R.ok(emrDetail);
if (emrDetail != null) {
return R.ok(emrDetail);
}
// 如果门诊病历为空,检查住院病历(doc_record表)
DocRecord docRecord = docRecordService.getOne(
new LambdaQueryWrapper<DocRecord>()
.eq(DocRecord::getEncounterId, encounterId)
.orderByDesc(DocRecord::getCreateTime)
.last("LIMIT 1")
);
if (docRecord != null) {
// 住院病历存在,也返回数据
return R.ok(docRecord);
}
// 都没有病历
return R.ok(null);
}
/**

View File

@@ -250,7 +250,8 @@ public class AdviceSaveDto {
*/
public AdviceSaveDto() {
this.chineseHerbsDoseQuantity = new BigDecimal("1");
this.therapyEnum = TherapyTimeType.TEMPORARY.getValue();
// 默认设置为长期医嘱,但前端应该明确传递正确的值
this.therapyEnum = TherapyTimeType.LONG_TERM.getValue();
this.practitionerId = SecurityUtils.getLoginUser().getPractitionerId();
this.founderOrgId = SecurityUtils.getLoginUser().getOrgId(); // 开方人科室
}

View File

@@ -98,11 +98,14 @@ public class AdviceUtils {
for (AdviceInventoryDto inventoryDto : adviceInventory) {
// 匹配条件adviceDefinitionId, adviceTableName, locationId, lotNumber 同时相等
// 如果选择了具体的批次号,校验库存时需要加上批次号的匹配条件
// 🔧 Bug #177 修复:添加容错处理,如果 adviceTableName 为空则跳过该项匹配
boolean lotNumberMatch = StringUtils.isEmpty(saveDto.getLotNumber())
|| saveDto.getLotNumber().equals(inventoryDto.getLotNumber());
boolean tableNameMatch = StringUtils.isEmpty(saveDto.getAdviceTableName())
|| inventoryDto.getItemTable().equals(saveDto.getAdviceTableName());
// if (saveDto.)
if (inventoryDto.getItemId().equals(saveDto.getAdviceDefinitionId())
&& inventoryDto.getItemTable().equals(saveDto.getAdviceTableName())
&& tableNameMatch
&& inventoryDto.getLocationId().equals(saveDto.getLocationId()) && lotNumberMatch) {
matched = true;
// 检查库存是否充足

View File

@@ -245,15 +245,37 @@ public class DocDefinitionAppServiceImpl implements IDocDefinitionAppService {
public R<?> getTreeList(DocDefinitonParam param) {
// 1. 获取当前登录用户的医院ID避免跨医院查询
Long hospitalId = SecurityUtils.getLoginUser().getHospitalId();
Long organizationId = param.getOrganizationId();
List<Integer> useRanges = param.getUseRanges();
log.info("获取文书定义树形列表 - 请求参数: hospitalId={}, organizationId={}, useRanges={}, name={}, primaryMenuEnum={}",
hospitalId, organizationId, useRanges, param.getName(), param.getPrimaryMenuEnum());
if (hospitalId == null) {
log.warn("当前登录用户未关联医院ID将使用默认值");
// 设置默认医院ID为1或其他合适的默认值
hospitalId = 1L;
}
if (organizationId == null || organizationId == 0) {
log.warn("organizationId为空或0将跳过医院过滤和使用范围过滤");
}
if (useRanges == null || useRanges.isEmpty()) {
log.warn("useRanges为空可能返回所有使用范围的文书");
}
// 2. 数据库查询文书定义列表
List<DocDefinitionDto> docList = docDefinitionAppMapper.getDefinationList(param.getUseRanges(),
param.getOrganizationId(), hospitalId, param.getName(), param.getPrimaryMenuEnum());
List<DocDefinitionDto> docList = docDefinitionAppMapper.getDefinationList(useRanges,
organizationId, hospitalId, param.getName(), param.getPrimaryMenuEnum());
log.info("获取文书定义树形列表 - 查询结果: 记录数={}", docList != null ? docList.size() : 0);
if (docList != null && !docList.isEmpty()) {
for (DocDefinitionDto doc : docList) {
log.debug("文书: id={}, name={}, useRangeEnum={}, hospitalId={}",
doc.getId(), doc.getName(), doc.getUseRangeEnum(), doc.getHospitalId());
}
}
// 3. 构建树形结构(空列表时返回空树,避免空指针)
List<DirectoryNode> treeNodes = new ArrayList<>();

View File

@@ -106,7 +106,9 @@ public class DocDefinitionController {
* @return
*/
@GetMapping("/treeList")
public R<?> getTreeList(DocDefinitonParam docDefinitonParam) {
public R<?> getTreeList(DocDefinitonParam docDefinitonParam,
@RequestParam(value = "useRanges", required = false) List<Integer> useRanges) {
docDefinitonParam.setUseRanges(useRanges);
return iDocDefinitionAppService.getTreeList(docDefinitonParam);
}

View File

@@ -40,8 +40,10 @@ import com.openhis.web.inhospitalnursestation.mapper.ATDManageAppMapper;
import com.openhis.workflow.domain.ServiceRequest;
import com.openhis.workflow.service.IServiceRequestService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
@@ -53,6 +55,7 @@ import java.util.stream.Stream;
* @author zwh
* @date 2025-07-28
*/
@Slf4j
@Service
public class ATDManageAppServiceImpl implements IATDManageAppService {
@@ -283,6 +286,7 @@ public class ATDManageAppServiceImpl implements IATDManageAppService {
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public R<?> admissionBedAssignment(AdmissionPatientInfoDto admissionPatientInfoDto) {
// 住院id
@@ -298,31 +302,39 @@ public class ATDManageAppServiceImpl implements IATDManageAppService {
// 进入编辑患者信息,不更换床位
if (Whether.YES.getCode().equals(admissionPatientInfoDto.getEditFlag())) {
// 更新患者病情(如果提供了)
if (admissionPatientInfoDto.getPriorityEnum() != null) {
// 更新患者病情
encounterService.updatePriorityEnumById(encounterId, admissionPatientInfoDto.getPriorityEnum());
// 将之前的住院参与者更新为已完成(如果存在的话)
encounterParticipantService.updateEncounterParticipantsStatus(encounterId);
// 更新住院参与者
// 住院医生
}
// 将之前的住院参与者更新为已完成(如果存在的话)
encounterParticipantService.updateEncounterParticipantsStatus(encounterId);
// 更新住院参与者
// 住院医生(必须填写)
if (admissionPatientInfoDto.getAdmittingDoctorId() != null) {
encounterParticipantService.creatEncounterParticipants(encounterId, startTime,
admissionPatientInfoDto.getAdmittingDoctorId(), ParticipantType.ADMITTING_DOCTOR.getCode());
// 责任护士
}
// 责任护士(必须填写)
if (admissionPatientInfoDto.getPrimaryNurseId() != null) {
encounterParticipantService.creatEncounterParticipants(encounterId, startTime,
admissionPatientInfoDto.getPrimaryNurseId(), ParticipantType.PRIMARY_NURSE.getCode());
if (admissionPatientInfoDto.getAttendingDoctorId() != null) {
// 主治医生
encounterParticipantService.creatEncounterParticipants(encounterId, startTime,
admissionPatientInfoDto.getAttendingDoctorId(), ParticipantType.ATTENDING_DOCTOR.getCode());
}
if (admissionPatientInfoDto.getChiefDoctorId() != null) {
// 主任医生
encounterParticipantService.creatEncounterParticipants(encounterId, startTime,
admissionPatientInfoDto.getChiefDoctorId(), ParticipantType.CHIEF_DOCTOR.getCode());
}
}
// 更新入院体征
saveOrUpdateAdmissionSigns(encounterId, admissionPatientInfoDto, startTime);
// 主治医生(可选)
if (admissionPatientInfoDto.getAttendingDoctorId() != null) {
encounterParticipantService.creatEncounterParticipants(encounterId, startTime,
admissionPatientInfoDto.getAttendingDoctorId(), ParticipantType.ATTENDING_DOCTOR.getCode());
}
// 主任医生(可选)
if (admissionPatientInfoDto.getChiefDoctorId() != null) {
encounterParticipantService.creatEncounterParticipants(encounterId, startTime,
admissionPatientInfoDto.getChiefDoctorId(), ParticipantType.CHIEF_DOCTOR.getCode());
}
// 更新入院体征(在事务外执行,避免影响参与者数据保存)
try {
saveOrUpdateAdmissionSigns(encounterId, admissionPatientInfoDto, startTime);
} catch (Exception e) {
log.error("保存入院体征失败,但不影响参与者数据", e);
}
return R.ok("患者信息更新成功");
}
@@ -421,8 +433,12 @@ public class ATDManageAppServiceImpl implements IATDManageAppService {
if (result == 0) {
return R.fail("床位分配失败,请联系管理员");
}
// 保存入院体征
saveOrUpdateAdmissionSigns(encounterId, admissionPatientInfoDto, startTime);
// 保存入院体征(在事务外执行,避免影响参与者数据保存)
try {
saveOrUpdateAdmissionSigns(encounterId, admissionPatientInfoDto, startTime);
} catch (Exception e) {
log.error("保存入院体征失败,但不影响参与者数据", e);
}
return R.ok("床位分配成功");
}

View File

@@ -69,7 +69,8 @@ public class PatientHomeAppServiceImpl implements IPatientHomeAppService {
// 分页查询,查询患者信息
IPage<PatientHomeDto> patientHomeInfoPage = patientHomeAppMapper.getPage(new Page<>(pageNo, pageSize),
patientHomeSearchParam.getStatusEnum(), patientHomeSearchParam.getPatientId(), searchKey, queryWrapper);
patientHomeSearchParam.getStatusEnum(), patientHomeSearchParam.getPatientId(), searchKey,
patientHomeSearchParam.getNursingLevelList(), queryWrapper);
patientHomeInfoPage.getRecords().forEach(e -> {
// 护理级别

View File

@@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
/**
* 患者首页 应用实现
@@ -36,7 +37,11 @@ public class PatientHomeController {
public R<?> getPatientInfoInit(PatientHomeSearchParam patientHomeSearchParam,
@RequestParam(value = "searchKey", defaultValue = "") String searchKey,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize, HttpServletRequest request) {
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize,
@RequestParam(value = "nursingLevelList", required = false) List<Integer> nursingLevelList,
HttpServletRequest request) {
// 将护理级别列表设置到查询参数中
patientHomeSearchParam.setNursingLevelList(nursingLevelList);
return patientHomeAppService.getPatientInfoInit(patientHomeSearchParam, searchKey, pageNo, pageSize, request);
}

View File

@@ -3,6 +3,8 @@ package com.openhis.web.inpatientmanage.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 患者首页查询参数
*
@@ -19,4 +21,7 @@ public class PatientHomeSearchParam {
/** 患者ID */
private Long patientId;
/** 护理级别列表(多选) */
private List<Integer> nursingLevelList;
}

View File

@@ -27,10 +27,13 @@ public interface PatientHomeAppMapper {
* @param statusEnum 状态编码
* @param patientId 患者ID
* @param searchKey 查询条件
* @param nursingLevelList 护理级别列表
* @param queryWrapper 查询wrapper
* @return 住院登记信息
*/
IPage<PatientHomeDto> getPage(@Param("page") Page<PatientHomeDto> page, @Param("statusEnum") Integer statusEnum,
@Param("patientId") Long patientId, @Param("searchKey") String searchKey,
@Param("nursingLevelList") List<Integer> nursingLevelList,
@Param(Constants.WRAPPER) QueryWrapper<PatientHomeDto> queryWrapper);
/**

View File

@@ -1,6 +1,7 @@
package com.openhis.web.lab.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.core.common.core.controller.BaseController;
import com.core.common.core.domain.AjaxResult;
import com.openhis.lab.domain.InspectionType;
@@ -29,7 +30,25 @@ public class InspectionTypeController extends BaseController {
private final TransactionTemplate transactionTemplate;
/**
* 获取检验类型列表
* 分页获取检验类型列表
*
* @param pageNo 页码
* @param pageSize 每页数量
* @param searchKey 搜索关键词
*/
@GetMapping("/page")
public AjaxResult getPage(
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "100") Integer pageSize,
@RequestParam(value = "searchKey", required = false) String searchKey) {
log.info("【检验类型】分页查询请求 - pageNo: {}, pageSize: {}, searchKey: {}", pageNo, pageSize, searchKey);
IPage<InspectionType> result = inspectionTypeService.getPage(pageNo, pageSize, searchKey);
log.info("【检验类型】分页查询完成 - 总记录数: {}, 当前页记录数: {}", result.getTotal(), result.getRecords().size());
return AjaxResult.success(result);
}
/**
* 获取检验类型列表(不分页,兼容旧接口)
*/
@GetMapping("/list")
public AjaxResult list(InspectionType inspectionType) {

View File

@@ -62,10 +62,10 @@ public class OutpatientInfusionAppServiceImpl implements IOutpatientInfusionAppS
OutpatientStationInitDto initDto = new OutpatientStationInitDto();
// 执行状态
List<OutpatientStationInitDto.ServiceStatus> serviceStatusOptions = new ArrayList<>();
serviceStatusOptions.add(new OutpatientStationInitDto.ServiceStatus(RequestStatus.ACTIVE.getValue(),
"待执行"));
serviceStatusOptions.add(new OutpatientStationInitDto.ServiceStatus(RequestStatus.COMPLETED.getValue(),
RequestStatus.COMPLETED.getInfo()));
// serviceStatusOptions.add(new OutpatientStationInitDto.ServiceStatus(RequestStatus.IN_PROGRESS.getValue(),
// RequestStatus.IN_PROGRESS.getInfo()));
serviceStatusOptions.add(new OutpatientStationInitDto.ServiceStatus(RequestStatus.CANCELLED.getValue(),
RequestStatus.CANCELLED.getInfo()));
initDto.setServiceStatusOptions(serviceStatusOptions);

View File

@@ -622,6 +622,37 @@ public class PaymentRecServiceImpl implements IPaymentRecService {
});
if (!medicationRequestIdList.isEmpty()) {
// 获取药品请求信息,为输液类药品生成服务请求
List<MedicationRequest> medicationRequests = medicationRequestService.listByIds(medicationRequestIdList);
// 为输液类药品生成 wor_service_request 记录
for (MedicationRequest medReq : medicationRequests) {
if (medReq.getInfusionFlag() != null && medReq.getInfusionFlag() == 1) {
ServiceRequest serviceRequest = new ServiceRequest();
serviceRequest.setBasedOnId(medReq.getId())
.setBasedOnTable(CommonConstants.TableName.MED_MEDICATION_REQUEST)
.setEncounterId(medReq.getEncounterId())
.setPatientId(medReq.getPatientId())
.setActivityId(medReq.getMedicationId())
.setStatusEnum(RequestStatus.ACTIVE.getValue()) // 状态设为已发送 (2),这样门诊输液页面才能查到
.setGroupId(medReq.getGroupId())
.setOrgId(medReq.getOrgId())
.setRequesterId(medReq.getPractitionerId())
.setAuthoredTime(new Date())
.setEncounterDiagnosisId(medReq.getEncounterDiagnosisId())
.setConditionId(medReq.getConditionId())
.setQuantity(medReq.getQuantity())
.setUnitCode(medReq.getUnitCode())
.setPriorityEnum(medReq.getPriorityEnum())
.setPerformFlag(Whether.NO.getValue())
.setIntentEnum(medReq.getIntentEnum())
.setCategoryEnum(medReq.getCategoryEnum())
.setYbClassEnum(medReq.getYbClassEnum())
.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.SERVICE_RES_NO.getPrefix(), 4));
serviceRequestService.save(serviceRequest);
}
}
// 更新请求状态为已完成
medicationRequestService.updateCompletedStatusBatch(medicationRequestIdList, null, null);
// 更新药品发放状态为待配药

View File

@@ -236,6 +236,11 @@ public class WesternMedicineDispenseAppServiceImpl implements IWesternMedicineDi
EnumUtils.getInfoByValue(DispenseStatus.class, medicineDispenseOrder.getStatusEnum()));
// 设置所在表名
medicineDispenseOrder.setItemTable(CommonConstants.TableName.MED_MEDICATION_DEFINITION);
// 处方类型(发药类型:门诊/住院等)
if (medicineDispenseOrder.getDispenseEnum() != null) {
medicineDispenseOrder.setDispenseEnum_enumText(
EnumUtils.getInfoByValue(EncounterClass.class, medicineDispenseOrder.getDispenseEnum()));
}
});
return R.ok(medicineDispenseOrderPage);
}

View File

@@ -271,6 +271,12 @@ public class ItemDispenseOrderDto {
private String medTypeCode;
private String medTypeCode_dictText;
/**
* 发药类型(处方类型:门诊/住院等)
*/
private Integer dispenseEnum;
private String dispenseEnum_enumText;
/**
* 输液标志
*/

View File

@@ -29,9 +29,12 @@ import com.openhis.web.regdoctorstation.appservice.IAdviceManageAppService;
import com.openhis.web.regdoctorstation.dto.*;
import com.openhis.web.regdoctorstation.mapper.AdviceManageAppMapper;
import com.openhis.web.regdoctorstation.utils.RegPrescriptionUtils;
import com.openhis.workflow.domain.ActivityDefinition;
import com.openhis.workflow.domain.DeviceRequest;
import com.openhis.workflow.domain.ServiceRequest;
import com.openhis.workflow.service.IActivityDefinitionService;
import com.openhis.workflow.domain.ActivityDefinition;
import com.openhis.workflow.service.IDeviceDispenseService;
import com.openhis.workflow.service.IDeviceRequestService;
import com.openhis.workflow.service.IServiceRequestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -76,6 +79,12 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
@Resource
IActivityDefinitionService iActivityDefinitionService;
@Resource
IDeviceRequestService iDeviceRequestService;
@Resource
IDeviceDispenseService iDeviceDispenseService;
/**
* 查询住院患者信息
*
@@ -167,12 +176,16 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
Long organizationId = regAdviceSaveParam.getOrganizationId();
// 医嘱分类信息
List<RegAdviceSaveDto> regAdviceSaveList = regAdviceSaveParam.getRegAdviceSaveList();
// 药品
List<RegAdviceSaveDto> medicineList = regAdviceSaveList.stream()
.filter(e -> ItemType.MEDICINE.getValue().equals(e.getAdviceType())).collect(Collectors.toList());
// 诊疗活动
List<RegAdviceSaveDto> activityList = regAdviceSaveList.stream()
.filter(e -> ItemType.ACTIVITY.getValue().equals(e.getAdviceType())).collect(Collectors.toList());
// 耗材 🔧 Bug #147 修复
List<RegAdviceSaveDto> deviceList = regAdviceSaveList.stream()
.filter(e -> ItemType.DEVICE.getValue().equals(e.getAdviceType())).collect(Collectors.toList());
// 保存时,校验临时医嘱库存
if (AdviceOpType.SAVE_ADVICE.getCode().equals(adviceOpType)) {
@@ -209,6 +222,11 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
*/
this.handService(activityList, startTime, authoredTime, curDate, adviceOpType, organizationId, signCode);
/**
* 🔧 Bug #147 修复:处理耗材请求
*/
this.handDevice(deviceList, startTime, authoredTime, curDate, adviceOpType, organizationId, signCode);
// 签发时,把草稿状态的账单更新为待收费
if (AdviceOpType.SIGN_ADVICE.getCode().equals(adviceOpType) && !regAdviceSaveList.isEmpty()) {
// 签发的医嘱id集合
@@ -593,6 +611,150 @@ public class AdviceManageAppServiceImpl implements IAdviceManageAppService {
}
/**
* 🔧 Bug #147 修复:处理耗材
*/
private void handDevice(List<RegAdviceSaveDto> deviceList, Date startTime, Date authoredTime, Date curDate,
String adviceOpType, Long organizationId, String signCode) {
// 当前登录账号的科室id
Long orgId = SecurityUtils.getLoginUser().getOrgId();
// 获取当前登录用户的tenantId
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
// 保存操作
boolean is_save = AdviceOpType.SAVE_ADVICE.getCode().equals(adviceOpType);
// 签发操作
boolean is_sign = AdviceOpType.SIGN_ADVICE.getCode().equals(adviceOpType);
// 删除
List<RegAdviceSaveDto> deleteList = deviceList.stream()
.filter(e -> DbOpType.DELETE.getCode().equals(e.getDbOpType())).collect(Collectors.toList());
for (RegAdviceSaveDto regAdviceSaveDto : deleteList) {
iDeviceRequestService.removeById(regAdviceSaveDto.getRequestId());
// 删除已经产生的耗材发放信息
iDeviceDispenseService.deleteDeviceDispense(regAdviceSaveDto.getRequestId());
// 删除费用项
iChargeItemService.deleteByServiceTableAndId(CommonConstants.TableName.WOR_DEVICE_REQUEST,
regAdviceSaveDto.getRequestId());
}
// 声明耗材请求
DeviceRequest deviceRequest;
// 声明费用项
ChargeItem chargeItem;
// 新增 + 修改 (长期医嘱)
List<RegAdviceSaveDto> longInsertOrUpdateList = deviceList.stream().filter(e -> TherapyTimeType.LONG_TERM
.getValue().equals(e.getTherapyEnum())
&& (DbOpType.INSERT.getCode().equals(e.getDbOpType()) || DbOpType.UPDATE.getCode().equals(e.getDbOpType())))
.collect(Collectors.toList());
for (RegAdviceSaveDto regAdviceSaveDto : longInsertOrUpdateList) {
deviceRequest = new DeviceRequest();
deviceRequest.setId(regAdviceSaveDto.getRequestId()); // 主键id
deviceRequest.setStatusEnum(is_save ? RequestStatus.DRAFT.getValue() : RequestStatus.ACTIVE.getValue()); // 请求状态
deviceRequest.setTenantId(SecurityUtils.getLoginUser().getTenantId()); // 显式设置租户ID
if (is_sign) {
deviceRequest.setReqAuthoredTime(authoredTime); // 医嘱签发时间
}
// 保存时处理的字段属性
if (is_save) {
deviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.DEVICE_RES_NO.getPrefix(), 4));
deviceRequest.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
deviceRequest.setQuantity(regAdviceSaveDto.getQuantity()); // 请求数量
deviceRequest.setUnitCode(regAdviceSaveDto.getUnitCode()); // 请求单位编码
deviceRequest.setLotNumber(regAdviceSaveDto.getLotNumber()); // 产品批号
deviceRequest.setCategoryEnum(regAdviceSaveDto.getCategoryEnum()); // 请求类型
deviceRequest.setDeviceDefId(regAdviceSaveDto.getAdviceDefinitionId());// 耗材定义id
deviceRequest.setPatientId(regAdviceSaveDto.getPatientId()); // 患者
deviceRequest.setRequesterId(regAdviceSaveDto.getPractitionerId()); // 开方医生
deviceRequest.setOrgId(regAdviceSaveDto.getFounderOrgId()); // 开方人科室
deviceRequest.setReqAuthoredTime(startTime); // 医嘱开始时间
deviceRequest.setPerformLocation(regAdviceSaveDto.getLocationId()); // 发放科室
deviceRequest.setEncounterId(regAdviceSaveDto.getEncounterId()); // 就诊id
deviceRequest.setPackageId(regAdviceSaveDto.getPackageId()); // 组套id
deviceRequest.setContentJson(regAdviceSaveDto.getContentJson()); // 请求内容json
deviceRequest.setYbClassEnum(regAdviceSaveDto.getYbClassEnum());// 类别医保编码
deviceRequest.setConditionId(regAdviceSaveDto.getConditionId()); // 诊断id
deviceRequest.setEncounterDiagnosisId(regAdviceSaveDto.getEncounterDiagnosisId()); // 就诊诊断id
}
iDeviceRequestService.saveOrUpdate(deviceRequest);
}
// 新增 + 修改 (临时医嘱)
List<RegAdviceSaveDto> tempInsertOrUpdateList = deviceList.stream().filter(e -> TherapyTimeType.TEMPORARY
.getValue().equals(e.getTherapyEnum())
&& (DbOpType.INSERT.getCode().equals(e.getDbOpType()) || DbOpType.UPDATE.getCode().equals(e.getDbOpType())))
.collect(Collectors.toList());
for (RegAdviceSaveDto regAdviceSaveDto : tempInsertOrUpdateList) {
deviceRequest = new DeviceRequest();
deviceRequest.setId(regAdviceSaveDto.getRequestId()); // 主键id
deviceRequest.setStatusEnum(is_save ? RequestStatus.DRAFT.getValue() : RequestStatus.ACTIVE.getValue()); // 请求状态
deviceRequest.setTenantId(SecurityUtils.getLoginUser().getTenantId()); // 显式设置租户ID
if (is_sign) {
deviceRequest.setReqAuthoredTime(authoredTime); // 医嘱签发时间
}
// 保存时处理的字段属性
if (is_save) {
deviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.DEVICE_RES_NO.getPrefix(), 4));
deviceRequest.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
deviceRequest.setQuantity(regAdviceSaveDto.getQuantity()); // 请求数量
deviceRequest.setUnitCode(regAdviceSaveDto.getUnitCode()); // 请求单位编码
deviceRequest.setLotNumber(regAdviceSaveDto.getLotNumber()); // 产品批号
deviceRequest.setCategoryEnum(regAdviceSaveDto.getCategoryEnum()); // 请求类型
deviceRequest.setDeviceDefId(regAdviceSaveDto.getAdviceDefinitionId());// 耗材定义id
deviceRequest.setPatientId(regAdviceSaveDto.getPatientId()); // 患者
deviceRequest.setRequesterId(regAdviceSaveDto.getPractitionerId()); // 开方医生
deviceRequest.setOrgId(regAdviceSaveDto.getFounderOrgId()); // 开方人科室
deviceRequest.setReqAuthoredTime(startTime); // 医嘱开始时间
deviceRequest.setPerformLocation(regAdviceSaveDto.getLocationId()); // 发放科室
deviceRequest.setEncounterId(regAdviceSaveDto.getEncounterId()); // 就诊id
deviceRequest.setPackageId(regAdviceSaveDto.getPackageId()); // 组套id
deviceRequest.setContentJson(regAdviceSaveDto.getContentJson()); // 请求内容json
deviceRequest.setYbClassEnum(regAdviceSaveDto.getYbClassEnum());// 类别医保编码
deviceRequest.setConditionId(regAdviceSaveDto.getConditionId()); // 诊断id
deviceRequest.setEncounterDiagnosisId(regAdviceSaveDto.getEncounterDiagnosisId()); // 就诊诊断id
}
iDeviceRequestService.saveOrUpdate(deviceRequest);
// 保存时,保存耗材费用项
if (is_save) {
// 处理耗材发放
Long dispenseId = iDeviceDispenseService.handleDeviceDispense(deviceRequest,
regAdviceSaveDto.getDbOpType());
// 保存耗材费用项
chargeItem = new ChargeItem();
chargeItem.setId(regAdviceSaveDto.getChargeItemId()); // 费用项id
chargeItem.setStatusEnum(ChargeItemStatus.DRAFT.getValue()); // 收费状态
chargeItem.setBusNo(AssignSeqEnum.CHARGE_ITEM_NO.getPrefix().concat(deviceRequest.getBusNo()));
chargeItem.setGenerateSourceEnum(GenerateSource.DOCTOR_PRESCRIPTION.getValue()); // 生成来源
chargeItem.setPatientId(regAdviceSaveDto.getPatientId()); // 患者
chargeItem.setContextEnum(regAdviceSaveDto.getAdviceType()); // 类型
chargeItem.setEncounterId(regAdviceSaveDto.getEncounterId()); // 就诊id
chargeItem.setDefinitionId(regAdviceSaveDto.getDefinitionId()); // 费用定价ID
chargeItem.setDefDetailId(regAdviceSaveDto.getDefinitionDetailId()); // 定价子表主键
chargeItem.setEntererId(regAdviceSaveDto.getPractitionerId());// 开立人ID
chargeItem.setEnteredDate(curDate); // 开立时间
chargeItem.setServiceTable(CommonConstants.TableName.WOR_DEVICE_REQUEST);// 医疗服务类型
chargeItem.setServiceId(deviceRequest.getId()); // 医疗服务ID
chargeItem.setProductTable(regAdviceSaveDto.getAdviceTableName());// 产品所在表
chargeItem.setProductId(regAdviceSaveDto.getAdviceDefinitionId());// 收费项id
chargeItem.setAccountId(regAdviceSaveDto.getAccountId());// 关联账户ID
chargeItem.setRequestingOrgId(orgId); // 开立科室
chargeItem.setConditionId(regAdviceSaveDto.getConditionId()); // 诊断id
chargeItem.setEncounterDiagnosisId(regAdviceSaveDto.getEncounterDiagnosisId()); // 就诊诊断id
chargeItem.setDispenseId(dispenseId); // 发放ID
chargeItem.setQuantityValue(regAdviceSaveDto.getQuantity()); // 数量
chargeItem.setQuantityUnit(regAdviceSaveDto.getUnitCode()); // 单位
chargeItem.setUnitPrice(regAdviceSaveDto.getUnitPrice()); // 单价
chargeItem.setTotalPrice(regAdviceSaveDto.getTotalPrice()); // 总价
iChargeItemService.saveOrUpdate(chargeItem);
}
}
}
/**
* 查询住院医嘱请求数据
*

View File

@@ -30,16 +30,22 @@
T1.pricing_flag,
T1.sort_order,
T1.service_range,
T1.inspection_type_id,
T2.type_code as item_type_code,
T2.yb_type,
T2.price_code,
T2.price as retail_price,
T4.amount as maximum_retail_price
T4.amount as maximum_retail_price,
T3.name as test_type
FROM wor_activity_definition T1
/* 只JOIN必要的价格表使用INNER JOIN避免笛卡尔积 */
INNER JOIN adm_charge_item_definition T2
ON T1.id = T2.instance_id
AND T2.instance_table = 'wor_activity_definition'
/* 检验类型关联 */
LEFT JOIN inspection_type T3
ON T1.inspection_type_id = T3.id
AND T3.valid_flag = 1
/* 最高零售价使用LEFT JOIN因为可能不存在 */
LEFT JOIN adm_charge_item_def_detail T4
ON T4.definition_id = T2.id
@@ -49,10 +55,10 @@
<if test="ew.customSqlSegment != null and ew.customSqlSegment != ''">
<choose>
<when test="ew.customSqlSegment.contains('tenant_id')">
${ew.customSqlSegment.replaceFirst('tenant_id', 'T1.tenant_id').replaceFirst('status_enum', 'T1.status_enum').replaceFirst('WHERE', 'AND')}
${ew.customSqlSegment.replaceFirst('tenant_id', 'T1.tenant_id').replaceFirst('status_enum', 'T1.status_enum').replaceFirst('name', 'T1.name').replaceFirst('WHERE', 'AND')}
</when>
<otherwise>
${ew.customSqlSegment.replaceFirst('status_enum', 'T1.status_enum').replaceFirst('WHERE', 'AND')}
${ew.customSqlSegment.replaceFirst('status_enum', 'T1.status_enum').replaceFirst('name', 'T1.name').replaceFirst('WHERE', 'AND')}
</otherwise>
</choose>
</if>
@@ -97,9 +103,13 @@
T1.children_json,
T1.pricing_flag,
T1.sort_order,
T1.service_range
T1.service_range,
T1.inspection_type_id,
T3.name as test_type
FROM wor_activity_definition T1
LEFT JOIN adm_charge_item_definition T2 ON T1.id = T2.instance_id
/* 检验类型关联 */
LEFT JOIN inspection_type T3 ON T1.inspection_type_id = T3.id AND T3.valid_flag = 1
<where>
T1.delete_flag = '0'
AND T2.instance_table = 'wor_activity_definition'

View File

@@ -204,8 +204,16 @@
T1.yb_no AS yb_no,
'' AS product_name,
-- 前端"类型"列显示目录类别category_code
-- 将category_code转换为整数,用于字典转换字典转换框架会自动填充activityType_dictText
CAST(T1.category_code AS INTEGER) AS activity_type,
-- 🔧 Bug #177 修复:将category_code(中文)转换为对应的整数,用于字典转换
-- 检验->1, 检查->2, 护理->3, 手术->4, 其他->5
CASE T1.category_code
WHEN '检验' THEN 1
WHEN '检查' THEN 2
WHEN '护理' THEN 3
WHEN '手术' THEN 4
WHEN '其他' THEN 5
ELSE 0
END AS activity_type,
NULL AS activity_type_dictText,
-- 前端"包装单位"列显示使用单位permitted_unit_code
T1.permitted_unit_code AS unit_code,

View File

@@ -30,6 +30,7 @@
AND ddo.delete_flag = '0'
WHERE dd.delete_flag = '0'
AND dd.is_valid = 0
<!-- 关键:医院 + 科室联合可见 -->
<if test="organizationId != null and organizationId != 0">
@@ -80,6 +81,10 @@
AND dd.name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="primaryMenuEnum != null">
AND dd.primary_menu_enum = #{primaryMenuEnum}
</if>
GROUP BY dd.id, dd.primary_menu_enum, dd.sub_menu
ORDER BY dd.display_order

View File

@@ -16,13 +16,13 @@
T5.ward_location_id,
T5.bed_location_id
FROM (SELECT T1.tenant_id,
T1.id AS patient_id, --患者ID
T1.name AS patient_name, --患者姓名
T1.bus_no AS patient_bus_no, --病历号
T1.birth_date, --患者出生日期
T1.gender_enum, --患者性别
T1.organization_id AS org_id, --科室ID
T2.id AS encounter_id, --就诊ID
T1.id AS patient_id, --患者ID
T1.name AS patient_name, --患者姓名
T1.bus_no AS patient_bus_no, --病历号
T1.birth_date, --患者出生日期
T1.gender_enum, --患者性别
T2.organization_id AS org_id, --科室ID(从就诊表取)
T2.id AS encounter_id, --就诊ID
T2.start_time AS admissionDate, --入院日期
T3.location_id AS ward_location_id, --病区
T4.location_id AS bed_location_id --床号
@@ -31,33 +31,33 @@
ON T2.patient_id = T1.id
AND T2.class_enum = #{classEnum}
AND T2.delete_flag = '0'
INNER JOIN (SELECT encounter_id,
location_id,
form_enum,
delete_flag
FROM (SELECT encounter_id,
location_id,
form_enum,
delete_flag,
ROW_NUMBER() OVER (PARTITION BY encounter_id
ORDER BY
CASE
WHEN update_time IS NULL THEN create_time
ELSE update_time
END DESC) AS rn
FROM adm_encounter_location
WHERE form_enum = #{ward}
AND delete_flag = '0'
) ranked
WHERE rn = 1) AS T3
ON T3.encounter_id = T2.ID
INNER JOIN adm_encounter_location AS T4
INNER JOIN (SELECT encounter_id,
location_id,
form_enum,
delete_flag
FROM (SELECT encounter_id,
location_id,
form_enum,
delete_flag,
ROW_NUMBER() OVER (PARTITION BY encounter_id
ORDER BY
CASE
WHEN update_time IS NULL THEN create_time
ELSE update_time
END DESC) AS rn
FROM adm_encounter_location
WHERE form_enum = #{ward}
AND delete_flag = '0'
) ranked
WHERE rn = 1) AS T3
ON T3.encounter_id = T2.ID
LEFT JOIN adm_encounter_location AS T4
ON T4.encounter_id = T2.ID
AND T4.form_enum = #{bed}
AND T4.status_enum = #{active}
AND T4.delete_flag = '0'
WHERE T1.delete_flag = '0'
ORDER BY T4.location_id ASC) AS T5
WHERE T1.delete_flag = '0'
ORDER BY T4.location_id ASC) AS T5
${ew.customSqlSegment}
</select>
@@ -79,12 +79,12 @@
T6.context_json,
T6.bed_location_id
FROM (SELECT T1.tenant_id,
T1.id AS patient_id, --患者ID
T1.name AS patient_name, --患者姓名
T1.birth_date, --患者出生日期
T1.gender_enum, --患者性别
T1.organization_id AS org_id, --科室ID
T2.id AS encounter_id, --就诊ID
T1.id AS patient_id, --患者ID
T1.name AS patient_name, --患者姓名
T1.birth_date, --患者出生日期
T1.gender_enum, --患者性别
T2.organization_id AS org_id, --科室ID(从就诊表取)
T2.id AS encounter_id, --就诊ID
T2.start_time AS admissionDate, --入院日期
T3.location_id AS ward_location_id, --病区
T5.id AS record_id, --记录单ID
@@ -97,32 +97,32 @@
ON T2.patient_id = T1.id
AND T2.class_enum = #{classEnum}
AND T2.delete_flag = '0'
INNER JOIN (SELECT encounter_id,
location_id,
form_enum,
delete_flag
FROM (SELECT encounter_id,
location_id,
form_enum,
delete_flag,
ROW_NUMBER() OVER (PARTITION BY encounter_id
ORDER BY
CASE
WHEN update_time IS NULL THEN create_time
ELSE update_time
END DESC) AS rn
FROM adm_encounter_location
WHERE form_enum = #{ward}
AND delete_flag = '0'
) ranked
WHERE rn = 1) AS T3
ON T3.encounter_id = T2.ID
INNER JOIN adm_encounter_location AS T4
INNER JOIN (SELECT encounter_id,
location_id,
form_enum,
delete_flag
FROM (SELECT encounter_id,
location_id,
form_enum,
delete_flag,
ROW_NUMBER() OVER (PARTITION BY encounter_id
ORDER BY
CASE
WHEN update_time IS NULL THEN create_time
ELSE update_time
END DESC) AS rn
FROM adm_encounter_location
WHERE form_enum = #{ward}
AND delete_flag = '0'
) ranked
WHERE rn = 1) AS T3
ON T3.encounter_id = T2.ID
LEFT JOIN adm_encounter_location AS T4
ON T4.encounter_id = T2.ID
AND T4.form_enum = #{bed}
AND T4.status_enum = #{active}
AND T4.delete_flag = '0'
LEFT JOIN doc_emr AS T5
LEFT JOIN doc_emr AS T5
ON T5.patient_id = T1.id
AND T5.encounter_id = T2.ID
AND T5.class_enum = 1 --护理记录单

View File

@@ -242,6 +242,14 @@
)
)
</if>
-- 护理级别筛选(多选)
<if test="nursingLevelList != null and nursingLevelList.size() > 0">
AND T2.priority_enum IN
<foreach collection="nursingLevelList" item="level" open="(" separator="," close=")">
#{level}
</foreach>
</if>
) patient_base
) ranked_result
WHERE rn = 1

View File

@@ -34,14 +34,16 @@
ON e.id = sr.encounter_id
AND sr.refund_service_id IS NULL
AND sr.delete_flag = '0'
INNER JOIN med_medication_request mmr
ON mmr.id = sr.based_on_id
AND mmr.delete_flag = '0'
AND mmr.infusion_flag = 1
LEFT JOIN adm_patient pt
ON pt.id = sr.patient_id
AND pt.delete_flag = '0'
LEFT JOIN wor_activity_definition ad
ON ad.id = sr.activity_id
AND ad.delete_flag = '0'
WHERE e.delete_flag = '0'
AND sr.status_enum IN (#{inProgress}, #{completed}, #{cancelled})
AND sr.based_on_table = #{medMedicationRequest}
GROUP BY sr.status_enum,
sr.encounter_id,
e.bus_no,
@@ -106,7 +108,7 @@
mmr.performer_id,
dis.status_enum AS dispense_status,
mmd."name" AS medication_name,
ad."name" AS service_name,
mmd."name" AS service_name,
ap."name" AS practitioner_name,
o."name" AS dept_name,
-- 新增子查询:查询配药人
@@ -128,15 +130,12 @@
AND wsr.delete_flag = '0'
AND wsr.refund_service_id IS NULL
LEFT JOIN med_medication_request mmr
ON mmr.group_id = wsr.group_id
ON mmr.id = wsr.based_on_id
AND mmr.delete_flag = '0'
AND mmr.infusion_flag = 1
LEFT JOIN med_medication_dispense dis
ON dis.med_req_id = mmr.id
AND dis.delete_flag = '0'
LEFT JOIN wor_activity_definition ad
ON ad.id = wsr.activity_id
AND ad.delete_flag = '0'
LEFT JOIN med_medication_definition mmd
ON mmr.medication_id = mmd.id
AND mmd.delete_flag = '0'
@@ -147,7 +146,6 @@
ON o.id = wsr.org_id
AND o.delete_flag = '0'
WHERE ae.id = #{encounterId}
AND ad.category_code = '21'
AND wsr.based_on_table = #{medMedicationRequest}
AND ae.delete_flag = '0') AS pr
${ew.customSqlSegment}
@@ -173,13 +171,16 @@
wsr.occurrence_end_time,
wsr.id AS service_id,
wsr.tenant_id,
ad."name" AS service_name,
mmd."name" AS service_name,
ap."name" AS performer_name,
al."name" AS org_name
FROM wor_service_request wsr
LEFT JOIN wor_activity_definition ad
ON ad.id = wsr.activity_id
AND ad.delete_flag = '0'
LEFT JOIN med_medication_request mmr
ON wsr.based_on_id = mmr.id
AND mmr.delete_flag = '0'
LEFT JOIN med_medication_definition mmd
ON mmr.medication_id = mmd.id
AND mmd.delete_flag = '0'
LEFT JOIN adm_practitioner ap
ON wsr.performer_id = ap.id
AND ap.delete_flag = '0'

View File

@@ -41,6 +41,7 @@
<result property="merchandiseName" column="merchandise_name"/>
<result property="traceNo" column="trace_no"/>
<result property="partAttributeEnum" column="part_attribute_enum"/>
<result property="dispenseEnum" column="dispense_enum"/>
<collection property="inventoryDetailList" ofType="com.openhis.web.pharmacymanage.dto.InventoryDetailDto">
<result property="inventoryId" column="inventory_id"/>
<result property="maxUnitCode" column="max_unit_code"/>
@@ -169,7 +170,8 @@
ii.inventory_quantity,
ii.inventory_lot_number,
ii.expiration_date,
ii.med_type_code
ii.med_type_code,
ii.dispense_enum
FROM ( SELECT T8."name" AS department_name,
T9.id AS doctor_id,
T9."name" AS doctor_name,
@@ -188,7 +190,7 @@
T1.status_enum,
T2.rate_code,
T1.location_id,
T1.method_code,
T2.method_code,
T1.lot_number,
T2.dose_unit_code,
T2.dispense_per_quantity,
@@ -216,7 +218,8 @@
T14.quantity AS inventory_quantity,
T14.lot_number AS inventory_lot_number,
T14.expiration_date,
T15.med_type_code
T15.med_type_code,
T1.dispense_enum
FROM med_medication_dispense AS T1
LEFT JOIN med_medication_request AS T2
ON T1.med_req_id = T2.id
@@ -252,7 +255,7 @@
ON T1.location_id = T13.id
AND T13.delete_flag = '0'
LEFT JOIN wor_inventory_item AS T14
ON T2.medication_id = T14.item_id
ON T1.medication_id = T14.item_id
AND T1.location_id = T14.location_id
AND T14.delete_flag = '0'
LEFT JOIN adm_encounter_diagnosis AS T15

View File

@@ -10,6 +10,7 @@ import com.openhis.administration.mapper.EncounterParticipantMapper;
import com.openhis.administration.service.IEncounterParticipantService;
import com.openhis.common.enums.EncounterActivityStatus;
import com.openhis.common.enums.ParticipantType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@@ -22,6 +23,7 @@ import java.util.List;
* @author system
* @date 2025-02-20
*/
@Slf4j
@Service
public class EncounterParticipantServiceImpl extends ServiceImpl<EncounterParticipantMapper, EncounterParticipant>
implements IEncounterParticipantService {
@@ -58,9 +60,12 @@ public class EncounterParticipantServiceImpl extends ServiceImpl<EncounterPartic
@Override
public void creatEncounterParticipants(Long encounterId, Date startTime, Long practitionerId, String typeCode) {
EncounterParticipant encounterParticipant = new EncounterParticipant();
encounterParticipant.setEncounterId(encounterId).setStartTime(startTime).setPractitionerId(practitionerId)
.setTypeCode(typeCode);
baseMapper.insert(encounterParticipant);
encounterParticipant.setEncounterId(encounterId)
.setStartTime(startTime)
.setPractitionerId(practitionerId)
.setTypeCode(typeCode)
.setStatusEnum(EncounterActivityStatus.ACTIVE.getValue());
int result = baseMapper.insert(encounterParticipant);
}
/**
@@ -75,7 +80,9 @@ public class EncounterParticipantServiceImpl extends ServiceImpl<EncounterPartic
return baseMapper.update(null,
new LambdaUpdateWrapper<EncounterParticipant>()
.set(EncounterParticipant::getStatusEnum, EncounterActivityStatus.COMPLETED.getValue())
.eq(EncounterParticipant::getEncounterId, encounterId).in(EncounterParticipant::getTypeCode,
.eq(EncounterParticipant::getEncounterId, encounterId)
.eq(EncounterParticipant::getStatusEnum, EncounterActivityStatus.ACTIVE.getValue())
.in(EncounterParticipant::getTypeCode,
ParticipantType.ATTENDING_DOCTOR.getCode(), ParticipantType.CHIEF_DOCTOR.getCode(),
ParticipantType.PRIMARY_NURSE.getCode(), ParticipantType.ADMITTING_DOCTOR.getCode()));
}

View File

@@ -1,5 +1,7 @@
package com.openhis.lab.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.openhis.lab.domain.InspectionType;
@@ -11,4 +13,13 @@ import com.openhis.lab.domain.InspectionType;
*/
public interface IInspectionTypeService extends IService<InspectionType> {
/**
* 分页查询检验类型列表
*
* @param pageNo 页码
* @param pageSize 每页数量
* @param searchKey 搜索关键词(可选)
* @return 分页结果
*/
IPage<InspectionType> getPage(Integer pageNo, Integer pageSize, String searchKey);
}

View File

@@ -1,10 +1,14 @@
package com.openhis.lab.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.openhis.lab.domain.InspectionType;
import com.openhis.lab.mapper.InspectionTypeMapper;
import com.openhis.lab.service.IInspectionTypeService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
/**
* 检验类型Service实现类
@@ -15,4 +19,22 @@ import org.springframework.stereotype.Service;
@Service
public class InspectionTypeServiceImpl extends ServiceImpl<InspectionTypeMapper, InspectionType> implements IInspectionTypeService {
@Override
public IPage<InspectionType> getPage(Integer pageNo, Integer pageSize, String searchKey) {
Page<InspectionType> page = new Page<>(pageNo, pageSize);
LambdaQueryWrapper<InspectionType> queryWrapper = new LambdaQueryWrapper<>();
// 搜索关键词(按编码或名称模糊查询)
if (StringUtils.hasText(searchKey)) {
queryWrapper.and(wrapper -> wrapper
.like(InspectionType::getCode, searchKey)
.or()
.like(InspectionType::getName, searchKey));
}
// 按排序字段升序排列
queryWrapper.orderByAsc(InspectionType::getSortOrder);
return this.page(page, queryWrapper);
}
}

View File

@@ -89,4 +89,7 @@ public class ActivityDefinition extends HisBaseEntity {
/** 服务范围 */
private String serviceRange;
/** 检验类型ID关联 inspection_type 表) */
private Long inspectionTypeId;
}

View File

@@ -1,6 +1,6 @@
{
"name": "openhis",
"version": "3.8.8",
"version": "3.8.9",
"description": "OpenHIS管理系统",
"author": "OpenHIS",
"license": "MIT",

View File

@@ -1,10 +1,11 @@
/**
* 医疗常量配置
* 从字典动态获取常量值,避免硬编码
*
*
* 使用方式:
* import { DIAG_TYPE } from '@/utils/medicalConstants';
* import { DIAG_TYPE, RequestStatus } from '@/utils/medicalConstants';
* medTypeCode: DIAG_TYPE.WESTERN_MEDICINE
* serviceStatus: RequestStatus.ACTIVE
*/
import { getDicts } from '@/api/system/dict/data';
@@ -12,6 +13,52 @@ import { getDicts } from '@/api/system/dict/data';
// 诊断类型字典缓存
let diagTypeCache = null;
/**
* 请求状态枚举(与后端 RequestStatus.java 保持一致)
* 用于服务申请、医嘱执行等状态管理
*/
export const RequestStatus = {
/** 待发送 */
DRAFT: 1,
/** 已发送/待执行 */
ACTIVE: 2,
/** 已完成 */
COMPLETED: 3,
/** 暂停 */
ON_HOLD: 4,
/** 取消/待退 */
CANCELLED: 5,
/** 停嘱 */
STOPPED: 6,
/** 不执行 */
ENDED: 7,
/** 未知 */
UNKNOWN: 9,
};
/**
* 请求状态枚举的说明信息
*/
export const RequestStatusDescriptions = {
1: '待发送',
2: '已发送/待执行',
3: '已完成',
4: '暂停',
5: '取消/待退',
6: '停嘱',
7: '不执行',
9: '未知',
};
/**
* 获取请求状态的说明
* @param {number} value - 请求状态值
* @returns {string} - 说明信息
*/
export function getRequestStatusDescription(value) {
return RequestStatusDescriptions[value] || '未知状态';
}
/**
* 获取诊断类型字典(异步初始化)
*/

View File

@@ -241,11 +241,21 @@ export function tansParams(params) {
var part = encodeURIComponent(propName) + "=";
if (value !== null && value !== "" && typeof (value) !== "undefined") {
if (typeof value === 'object') {
for (const key of Object.keys(value)) {
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
let params = propName + '[' + key + ']';
var subPart = encodeURIComponent(params) + "=";
result += subPart + encodeURIComponent(value[key]) + "&";
// 处理数组类型
if (Array.isArray(value)) {
for (const item of value) {
if (item !== null && item !== "" && typeof (item) !== 'undefined') {
result += part + encodeURIComponent(item) + "&";
}
}
} else {
// 处理对象类型
for (const key of Object.keys(value)) {
if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
let params = propName + '[' + key + ']';
var subPart = encodeURIComponent(params) + "=";
result += subPart + encodeURIComponent(value[key]) + "&";
}
}
}
} else {

View File

@@ -467,7 +467,7 @@ function getDeptTree() {
/** 查询地点下拉树结构 */
function getLocationTree() {
locationTreeSelect({ formList: '11,16' }).then((response) => {
locationTreeSelect({ formList: '11,16,17' }).then((response) => {
locationOptions.value = response.data.records;
});
}

View File

@@ -175,6 +175,7 @@ import {
} from './component/api';
import AdviceListDialog from './component/adviceListDialog.vue';
import {formatDate, formatDateStr} from '@/utils/index';
import { RequestStatus } from '@/utils/medicalConstants';
const showSearch = ref(true);
const total = ref(1);
@@ -210,7 +211,7 @@ const data = reactive({
pageNo: 1,
pageSize: 10,
searchKey: undefined,
serviceStatus: 3, // 默认值为已完成 (对应 RequestStatus.COMPLETED)
serviceStatus: RequestStatus.ACTIVE, // 默认值为待执行
},
});
const { queryParams } = toRefs(data);

View File

@@ -359,6 +359,7 @@ async function getList() {
priceList: item.price ? [{ price: item.price }] : (item.retailPrice ? [{ price: item.retailPrice }] : []),
inventoryList: [],
adviceDefinitionId: item.id,
adviceTableName: 'adm_device_definition', // 🔧 Bug #177 修复:添加耗材表名,用于后端库存匹配
chargeItemDefinitionId: item.id,
positionId: item.locationId,
positionName: item.locationId_dictText || '',

View File

@@ -949,7 +949,23 @@ export function deleteInspectionApplication(applyNo) {
}
/**
* 获取检验类型列表(分类)
* 分页获取检验类型列表(分类)
* @param {Object} queryParams - 查询参数
* @param {number} queryParams.pageNo - 页码
* @param {number} queryParams.pageSize - 每页数量
* @param {string} queryParams.searchKey - 搜索关键词
*/
export function getInspectionTypePage(queryParams) {
return request({
url: '/system/inspection-type/page',
method: 'get',
params: queryParams,
});
}
/**
* 获取检验类型列表(不分页,兼容旧接口)
* @deprecated 建议使用 getInspectionTypePage 分页接口
*/
export function getInspectionTypeList() {
return request({
@@ -965,6 +981,7 @@ export function getInspectionTypeList() {
* @param {number} queryParams.pageNo - 页码
* @param {number} queryParams.pageSize - 每页数量
* @param {string} queryParams.categoryCode - 目录类别编码(检验)
* @param {string} queryParams.inspectionTypeId - 检验类型ID列表多个用逗号分隔
*/
export function getInspectionItemList(queryParams) {
return request({

View File

@@ -4,7 +4,7 @@
<!-- 顶部操作按钮区 -->
<el-header class="top-action-bar" height="50px">
<el-row class="action-buttons" type="flex" justify="end" :gutter="10">
<el-button type="success" size="large" @click="handleSave" class="save-btn">
<el-button type="success" size="large" @click="handleSave" class="save-btn" :loading="saving">
<el-icon><Document /></el-icon>
保存
</el-button>
@@ -400,19 +400,35 @@
<span class="card-title">检验项目选择</span>
</template>
<!-- 搜索框 -->
<el-input
<!-- 搜索框自动完成 -->
<el-autocomplete
v-model="searchKeyword"
:fetch-suggestions="querySearchInspectionItems"
placeholder="搜索检验项目..."
size="small"
clearable
prefix-icon="Search"
@input="handleSearch"
@select="handleSearchSelect"
@clear="handleSearchClear"
value-key="itemName"
class="search-input"
/>
:debounce="300"
>
<template #default="{ item }">
<div class="suggestion-item">
<span class="suggestion-name">{{ item.itemName }}</span>
<el-tag size="small" type="info" class="suggestion-category">{{ item.typeName || '检验' }}</el-tag>
<span class="suggestion-price">¥{{ item.itemPrice }}</span>
</div>
</template>
</el-autocomplete>
<!-- 分类树 -->
<el-scrollbar class="category-tree" style="max-height: 280px">
<el-scrollbar
class="category-tree"
style="max-height: 280px"
@scroll="handleScroll"
>
<!-- 无数据提示 -->
<el-empty v-if="!inspectionLoading && inspectionCategories.length === 0" description="暂无检验项目数据" :image-size="80" />
<!-- 数据列表 -->
@@ -427,9 +443,19 @@
>
<span class="category-tree-icon">{{ category.expanded ? '▼' : '▶' }}</span>
<span>{{ category.label }}</span>
<span class="category-count">({{ category.items.length }})</span>
<span class="category-count">({{ category.total || category.items.length }})</span>
<!-- 加载状态图标 -->
<el-icon v-if="category.loading" class="is-loading" style="margin-left: 8px; color: #409eff;">
<Loading />
</el-icon>
</div>
<div v-if="category.expanded" class="category-tree-children">
<!-- 加载中占位 -->
<div v-if="category.loading && category.items.length === 0" class="loading-placeholder">
<el-icon class="is-loading" style="margin-right: 8px;"><Loading /></el-icon>
<span>加载中...</span>
</div>
<!-- 项目列表 -->
<div
v-for="item in getFilteredItems(category.key)"
:key="item.itemId"
@@ -444,6 +470,22 @@
<span class="item-itemName">{{ item.itemName }}</span>
<span class="item-price">¥{{ item.itemPrice }}</span>
</div>
<!-- 加载更多 -->
<div v-if="category.hasMore && category.items.length > 0" class="load-more">
<el-button
link
size="small"
:loading="category.loading"
@click.stop="loadMoreItems(category.key)"
>
{{ category.loading ? '加载中...' : '加载更多' }}
</el-button>
<span class="load-info">(已加载 {{ category.items.length }}/{{ category.total }} )</span>
</div>
<!-- 加载完成提示 -->
<div v-if="!category.hasMore && category.items.length > 0" class="no-more">
已全部加载 ( {{ category.total }} )
</div>
</div>
</div>
</el-scrollbar>
@@ -490,9 +532,9 @@
</template>
<script setup>
import {onMounted, reactive, ref, watch, nextTick} from 'vue'
import {onMounted, reactive, ref, watch, computed, getCurrentInstance} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import { DocumentChecked, Plus, Document, Printer, Delete, Check } from '@element-plus/icons-vue'
import { DocumentChecked, Plus, Document, Printer, Delete, Check, Loading } from '@element-plus/icons-vue'
import {
checkInspectionApplicationNo,
deleteInspectionApplication, getApplyList,
@@ -502,8 +544,20 @@ import {
} from '../api'
import useUserStore from '@/store/modules/user.js'
// 迁移到 hiprint
import { simplePrint, PRINT_TEMPLATE, previewPrint } from '@/utils/printUtils.js'
import { previewPrint } from '@/utils/printUtils.js'
import {storeToRefs} from 'pinia'
import { debounce } from 'lodash-es'
// 获取当前组件实例和字典
const { proxy } = getCurrentInstance()
const { activity_category_code } = proxy.useDict('activity_category_code')
// 获取"检验"分类的字典值(与检验项目设置维护保持一致)
const inspectionCategoryCode = computed(() => {
const dictList = activity_category_code.value
const inspectionItem = dictList?.find(item => item.label === '检验')
return inspectionItem?.value || '22' // 默认使用数据库中检验的 category_code 值
})
// Props
const props = defineProps({
@@ -520,8 +574,8 @@ const props = defineProps({
const emit = defineEmits(['save'])
// 响应式数据
const showForm = ref(false)
const loading = ref(false)
const saving = ref(false) // 保存状态
const total = ref(0)
const leftActiveTab = ref('application')
const isGeneratingNewApplyNo = ref(false) // 标志:是否正在生成新申请单号
@@ -532,11 +586,8 @@ const { id: userId, name: userName, nickName: userNickName } = storeToRefs(userS
// 修改 initData 函数
async function initData() {
console.log('【检验】开始初始化数据当前patientInfo:', props.patientInfo)
// 先初始化患者信息(如果有)
if (props.patientInfo && props.patientInfo.encounterId) {
console.log('【检验】初始化患者信息')
queryParams.encounterId = props.patientInfo.encounterId
formData.visitNo = props.patientInfo.busNo || ''
formData.patientId = props.patientInfo.patientId || ''
@@ -550,20 +601,10 @@ async function initData() {
formData.applyOrganizationId = props.patientInfo.orgId || ''
formData.encounterId = props.patientInfo.encounterId
console.log('【检验】患者信息初始化完成formData:', JSON.stringify({
patientName: formData.patientName,
medicalrecordNumber: formData.medicalrecordNumber,
applyDepartment: formData.applyDepartment,
applyDocName: formData.applyDocName
}, null, 2))
// 生成申请单号
generateApplicationNo().then((newApplyNo) => {
formData.applyNo = newApplyNo;
console.log('【检验】申请单号生成:', newApplyNo)
});
} else {
console.log('【检验】没有有效的patientInfo跳过患者信息初始化')
formData.applyNo = newApplyNo
})
}
}
@@ -614,18 +655,6 @@ const formData = reactive({
encounterId: ''
})
// 表单验证规则
const formRules = {
natureofCost: [{ required: true, message: '请选择费用性质', trigger: 'change' }],
applyTime: [{ required: true, message: '请选择申请日期', trigger: 'change' }],
executeDepartment: [{ required: true, message: '请选择执行科室', trigger: 'change' }],
clinicDesc: [{ required: true, message: '请输入诊断描述', trigger: 'blur' }],
clinicDiag: [{ required: true, message: '请输入临床诊断', trigger: 'blur' }],
medicalHistorySummary: [{ required: true, message: '请输入病史摘要', trigger: 'blur' }],
purposeofInspection: [{ required: true, message: '请输入检验目的', trigger: 'blur' }],
labApplyItemList: [{ required: true, message: '请至少选择一个检验项目', trigger: 'change' }]
}
// 表单引用
const formRef = ref()
@@ -639,7 +668,8 @@ const validationErrors = reactive({
clinicDiag: false,
medicalHistorySummary: false,
purposeofInspection: false,
labApplyItemList: false
labApplyItemList: false,
applyTime: false
})
// 已选择的表格行
@@ -654,224 +684,276 @@ const searchKeyword = ref('')
// 活动分类
const activeCategory = ref('')
// 检验项目分类动态从API获取
// 检验项目分类动态从API获取,支持懒加载和分页
const inspectionCategories = ref([])
// 检验项目加载状态
// 检验项目加载状态(整体)
const inspectionLoading = ref(false)
// 加载检验项目分类和项目
// 每页加载条数
const PAGE_SIZE = 50
// 搜索防抖时间(毫秒)
const SEARCH_DEBOUNCE_TIME = 300
// 加载检验类型分类列表(只加载分类,项目懒加载)
async function loadInspectionData() {
// 如果已经加载过数据,直接返回(避免重复请求)
// 如果已经加载过分类,直接返回
if (inspectionCategories.value.length > 0) {
console.log('【检验】数据已缓存,跳过重复加载')
return
}
inspectionLoading.value = true
const startTime = Date.now() // 性能监控开始时间
try {
console.log('【检验】开始并行请求数据')
// 并行请求:同时获取检验类型列表和检验项目列表
const [typeRes, itemRes] = await Promise.all([
// 添加错误处理和重试机制
getInspectionTypeList().catch(error => {
console.error('【检验】获取检验类型失败:', error)
return { data: [] } // 返回空数据作为降级方案
}),
getInspectionItemList({
pageNo: 1,
pageSize: 200, // 进一步优化:减少数据量,按需加载
searchKey: '', // 添加搜索关键词参数
categoryCode: 'inspection' // 明确指定检验类别
}).catch(error => {
console.error('【检验】获取检验项目失败:', error)
return { data: { records: [] } } // 返回空数据作为降级方案
})
])
const endTime = Date.now()
console.log(`【检验】API请求完成耗时: ${endTime - startTime}ms`)
try {
// 只获取检验类型列表
const typeRes = await getInspectionTypeList().catch(error => {
console.error('获取检验类型失败:', error)
return { data: [] }
})
const typeList = typeRes.data || []
console.log('【检验】获取到检验类型数量:', typeList.length)
// 解析检验项目数据
let allItems = []
if (itemRes.data && itemRes.data.records) {
allItems = itemRes.data.records
} else if (itemRes.data && Array.isArray(itemRes.data)) {
allItems = itemRes.data
} else if (Array.isArray(itemRes)) {
allItems = itemRes
}
console.log('【检验】获取到检验项目数量:', allItems.length)
// 按分类组织数据
// 创建分类结构,但不加载项目(懒加载)
const categories = typeList
.filter(type => type.validFlag === 1 || type.validFlag === undefined)
.map((type, index) => {
const categoryItems = allItems
.filter(item => {
// 更宽松的过滤条件:如果没有明确的检验类别标识,也认为是检验项目
const isInspection = !item.categoryCode_dictText ||
!item.categoryName ||
!item.categoryCode ||
item.categoryCode_dictText === '检验' ||
item.categoryName === '检验' ||
item.categoryCode === 'inspection'
const matchType = item.typeName === type.name ||
item.inspectionTypeName === type.name ||
item.bigClassName === type.name ||
item.typeCode === type.code
return isInspection && (matchType || typeList.length === 1)
})
.map(item => ({
itemId: item.id || item.activityId || Math.random().toString(36).substr(2, 9),
itemName: item.name || item.itemName || '',
itemPrice: item.retailPrice || item.price || 0,
itemAmount: item.retailPrice || item.price || 0,
sampleType: item.sampleType || '血液',
unit: item.unit || '',
itemQty: 1,
serviceFee: 0,
type: type.name,
isSelfPay: false,
activityId: item.activityId,
code: item.code || item.activityCode
}))
.map((type, index) => ({
key: type.code || `type_${index}`,
label: type.name || `分类${index + 1}`,
typeId: type.id, // 保存类型ID用于分页查询
expanded: index === 0, // 默认展开第一个
loaded: false, // 是否已加载项目
loading: false, // 是否正在加载
items: [], // 项目列表
pageNo: 1, // 当前页码
pageSize: PAGE_SIZE, // 每页条数
total: 0, // 总条数
hasMore: true // 是否还有更多数据
}))
return {
key: type.code || `type_${index}`,
label: type.name || `分类${index + 1}`,
expanded: index === 0,
items: categoryItems
}
})
// 过滤掉没有项目的分类
const validCategories = categories.filter(cat => cat.items.length > 0)
// 如果没有有效分类,但有项目数据,创建一个默认分类
if (validCategories.length === 0 && allItems.length > 0) {
const defaultItems = allItems
.slice(0, 50) // 优化:限制显示数量
.map(item => ({
itemId: item.id || item.activityId || Math.random().toString(36).substr(2, 9),
itemName: item.name || item.itemName || '',
itemPrice: item.retailPrice || item.price || 0,
itemAmount: item.retailPrice || item.price || 0,
sampleType: item.sampleType || '血液',
unit: item.unit || '',
itemQty: 1,
serviceFee: 0,
type: '检验',
isSelfPay: false,
activityId: item.activityId,
code: item.code || item.activityCode
}))
if (defaultItems.length > 0) {
validCategories.push({
key: 'default',
label: '检验项目',
expanded: true,
items: defaultItems
})
}
}
if (validCategories.length > 0) {
inspectionCategories.value = validCategories
activeCategory.value = validCategories[0].key
console.log('【检验】数据加载成功,分类数量:', validCategories.length)
if (categories.length > 0) {
inspectionCategories.value = categories
activeCategory.value = categories[0].key
// 预加载第一个分类的项目
await loadCategoryItems(categories[0].key)
} else {
console.warn('【检验】未获取到有效的检验项目数据,使用备用数据')
// 直接使用备用数据,不抛出错误
inspectionCategories.value = [
{
key: 'biochemical',
label: '生化',
expanded: true,
items: [
{ itemId: 1, itemName: '肝功能', itemPrice: 31, itemAmount: 31, sampleType: '血清', unit: 'U/L', itemQty: 1, serviceFee: 0, type: '生化', isSelfPay: false },
{ itemId: 2, itemName: '肾功能', itemPrice: 28, itemAmount: 28, sampleType: '血清', unit: 'U/L', itemQty: 1, serviceFee: 0, type: '生化', isSelfPay: false },
{ itemId: 3, itemName: '血糖', itemPrice: 15, itemAmount: 15, sampleType: '血清', unit: 'mmol/L', itemQty: 1, serviceFee: 0, type: '生化', isSelfPay: false }
]
},
{
key: 'blood',
label: '临检',
expanded: false,
items: [
{ itemId: 4, itemName: '血常规+crp', itemPrice: 50, itemAmount: 50, sampleType: '全血', unit: '×10^9/L', itemQty: 1, serviceFee: 0, type: '血液', isSelfPay: false },
{ itemId: 5, itemName: '血常规(五分类)', itemPrice: 15, itemAmount: 15, sampleType: '全血', unit: '×10^9/L', itemQty: 1, serviceFee: 0, type: '血液', isSelfPay: false }
]
}
]
activeCategory.value = 'biochemical'
console.warn('未获取到检验类型分类')
}
} catch (error) {
console.error('加载检验项目数据失败:', error)
// 加载失败时使用静态数据作为备用
inspectionCategories.value = [
{
key: 'biochemical',
label: '生化',
expanded: true,
items: [
{ itemId: 1, itemName: '肝功能', itemPrice: 31, itemAmount: 31, sampleType: '血清', unit: 'U/L', itemQty: 1, serviceFee: 0, type: '生化', isSelfPay: false },
{ itemId: 2, itemName: '肾功能', itemPrice: 28, itemAmount: 28, sampleType: '血清', unit: 'U/L', itemQty: 1, serviceFee: 0, type: '生化', isSelfPay: false },
{ itemId: 3, itemName: '血糖', itemPrice: 15, itemAmount: 15, sampleType: '血清', unit: 'mmol/L', itemQty: 1, serviceFee: 0, type: '生化', isSelfPay: false }
]
},
{
key: 'blood',
label: '临检',
expanded: false,
items: [
{ itemId: 4, itemName: '血常规+crp', itemPrice: 50, itemAmount: 50, sampleType: '全血', unit: '×10^9/L', itemQty: 1, serviceFee: 0, type: '血液', isSelfPay: false },
{ itemId: 5, itemName: '血常规(五分类)', itemPrice: 15, itemAmount: 15, sampleType: '全血', unit: '×10^9/L', itemQty: 1, serviceFee: 0, type: '血液', isSelfPay: false }
]
}
]
activeCategory.value = 'biochemical'
console.error('加载检验类型分类失败:', error)
} finally {
inspectionLoading.value = false
}
}
// 获取过滤后的项目
// 懒加载分类项目(分页)
async function loadCategoryItems(categoryKey, loadMore = false) {
const category = inspectionCategories.value.find(c => c.key === categoryKey)
if (!category) return
// 已加载完成且不是加载更多,或正在加载中,跳过
if ((category.loaded && !loadMore) || category.loading) return
// 没有更多数据了,跳过
if (loadMore && !category.hasMore) return
category.loading = true
try {
const params = {
pageNo: category.pageNo,
pageSize: category.pageSize,
categoryCode: inspectionCategoryCode.value,
searchKey: searchKeyword.value || ''
}
// 如果有类型ID添加筛选条件
if (category.typeId) {
params.inspectionTypeId = category.typeId
}
const res = await getInspectionItemList(params)
// 解析数据
let records = []
let total = 0
if (res.data && res.data.records) {
records = res.data.records
total = res.data.total || 0
} else if (res.data && Array.isArray(res.data)) {
records = res.data
total = records.length
} else if (Array.isArray(res)) {
records = res
total = records.length
}
// 映射数据格式
const mappedItems = records.map(item => ({
itemId: item.id || item.activityId || Math.random().toString(36).substring(2, 11),
itemName: item.name || item.itemName || '',
itemPrice: item.retailPrice || item.price || 0,
itemAmount: item.retailPrice || item.price || 0,
sampleType: item.specimenCode_dictText || item.sampleType || '血液',
unit: item.unit || '',
itemQty: 1,
serviceFee: 0,
type: category.label,
isSelfPay: false,
activityId: item.activityId,
code: item.busNo || item.code || item.activityCode,
inspectionTypeId: item.inspectionTypeId || null
}))
// 更新分类数据
if (loadMore) {
// 追加数据
category.items.push(...mappedItems)
} else {
// 首次加载
category.items = mappedItems
}
category.total = total
category.hasMore = category.items.length < total
category.loaded = true
} catch (error) {
console.error(`加载分类 [${category.label}] 项目失败:`, error)
// 加载失败时设置空数据
if (!loadMore) {
category.items = []
category.total = 0
category.hasMore = false
category.loaded = true
}
} finally {
category.loading = false
}
}
// 加载更多项目
function loadMoreItems(categoryKey) {
const category = inspectionCategories.value.find(c => c.key === categoryKey)
if (!category || !category.hasMore || category.loading) return
category.pageNo++
loadCategoryItems(categoryKey, true)
}
// 处理滚动事件(无限滚动)
function handleScroll({ scrollTop, scrollHeight, clientHeight }) {
// 距离底部 50px 时触发加载更多
if (scrollHeight - scrollTop - clientHeight < 50) {
const expandedCategory = inspectionCategories.value.find(c => c.expanded)
if (expandedCategory && expandedCategory.hasMore && !expandedCategory.loading) {
loadMoreItems(expandedCategory.key)
}
}
}
// 防抖搜索处理
const handleSearchDebounced = debounce(() => {
// 重新加载当前展开分类的数据
const expandedCategory = inspectionCategories.value.find(c => c.expanded)
if (expandedCategory) {
// 重置分页状态
expandedCategory.pageNo = 1
expandedCategory.loaded = false
expandedCategory.hasMore = true
expandedCategory.items = []
// 重新加载
loadCategoryItems(expandedCategory.key)
}
}, SEARCH_DEBOUNCE_TIME)
// 获取过滤后的项目(本地搜索)
const getFilteredItems = (categoryKey) => {
const category = inspectionCategories.value.find(cat => cat.key === categoryKey)
if (!category) return []
if (!searchKeyword.value) {
// 如果正在加载,返回现有数据
if (category.loading) {
return category.items
}
return category.items.filter(item =>
item.itemName.toLowerCase().includes(searchKeyword.value.toLowerCase())
)
// 本地过滤(安全检查 itemName
if (searchKeyword.value) {
const keyword = searchKeyword.value.toLowerCase()
return category.items.filter(item =>
item.itemName && item.itemName.toLowerCase().includes(keyword)
)
}
return category.items
}
// 搜索建议查询(自动完成)
async function querySearchInspectionItems(queryString, cb) {
if (!queryString) {
cb([])
return
}
try {
const params = {
pageNo: 1,
pageSize: 20, // 限制返回数量
categoryCode: inspectionCategoryCode.value,
searchKey: queryString
}
const res = await getInspectionItemList(params)
let suggestions = []
if (res.data && res.data.records) {
// 映射数据格式,与 loadInspectionItemsByType 保持一致
suggestions = res.data.records.map(item => ({
itemId: item.id || item.activityId,
itemName: item.name || item.itemName || '',
itemPrice: item.retailPrice || item.price || 0,
sampleType: item.specimenCode_dictText || item.sampleType || '血液',
unit: item.unit || '',
code: item.busNo || item.code || item.activityCode,
activityId: item.activityId,
inspectionTypeId: item.inspectionTypeId || null
}))
}
cb(suggestions)
} catch (error) {
console.error('搜索检验项目失败:', error)
cb([])
}
}
// 搜索选择处理
function handleSearchSelect(item) {
// 直接添加到已选列表
if (!isItemSelected(item)) {
selectedInspectionItems.value.push({
...item,
itemName: item.itemName
})
}
// 清空搜索关键词
searchKeyword.value = ''
}
// 搜索框清空处理
function handleSearchClear() {
searchKeyword.value = ''
}
// 获取检验申请单列表
function getInspectionList() {
// 如果没有encounterId,不调用接口
if (!queryParams.encounterId) {
// console.warn('【检验】encounterId为空,不调用接口')
return
}
loading.value = true
// 调用分页API传递分页参数和 encounterId
// console.log('【检验】调用API,encounterId:', queryParams.encounterId, 'pageNo:', queryParams.pageNo, 'pageSize:', queryParams.pageSize)
getApplyList({
encounterId: queryParams.encounterId,
pageNo: queryParams.pageNo,
@@ -885,7 +967,7 @@ function getInspectionList() {
// 处理数据:将同一个申请单的多个明细合并成一条记录
inspectionList.value = mergeInspectionApplyRecords(res.data.records)
total.value = res.data.total || 0
}
}
// 如果返回的是普通数组
else if (Array.isArray(res.data)) {
// 处理数据:将同一个申请单的多个明细合并成一条记录
@@ -901,14 +983,12 @@ function getInspectionList() {
inspectionList.value = []
total.value = 0
}
// console.log('【检验】获取数据成功,数量:', inspectionList.value.length, '总数:', total.value)
} else {
inspectionList.value = []
total.value = 0
ElMessage.error(res.message || '获取检验申请单列表失败')
}
}).catch((error) => {
// console.error('获取检验申请单列表异常:', error)
inspectionList.value = []
total.value = 0
ElMessage.error('获取检验申请单列表异常: ' + (error.message || ''))
@@ -959,7 +1039,6 @@ function formatAmount(amount) {
// 新增申请单
async function handleNewApplication() {
// console.log('点击新增按钮')
resetForm()
// 生成新的申请单号
formData.applyNo = await generateApplicationNo()
@@ -977,15 +1056,11 @@ const checkApplicationNoExists = async (applyNo) => {
// 后端返回格式:如果存在返回{applyNo: "..."}不存在返回null
return response.code === 200 && response.data && response.data.applyNo;
} catch (error) {
// 隐藏具体的错误信息,仅记录在控制台
console.debug('检查申请单号时发生错误:', error.message);
// 如果API调用失败假设单号不存在以避免阻塞
return false;
}
};
// 生成申请单号
let counter = 0;
const generateApplicationNo = async () => {
let applyNo;
let isUnique = false;
@@ -1028,8 +1103,6 @@ const generateApplicationNo = async () => {
}
if (!isUnique) {
console.warn(`经过${maxAttempts}次尝试仍未生成唯一申请单号,使用备用方案`);
// 如果多次尝试后仍无法生成唯一单号使用UUID的一部分作为备用方案
const timestamp = Date.now().toString();
const randomPart = Math.floor(Math.random() * 1000).toString().padStart(3, '0');
const fallbackNo = 'F' + timestamp.slice(-17) + randomPart; // 使用F开头表示备用方案
@@ -1080,14 +1153,11 @@ async function resetForm() {
formRef.value?.clearValidate()
}
// 返回列表
function handleBack() {
showForm.value = false
getInspectionList()
}
// 保存
function handleSave() {
// 如果正在保存,直接返回
if (saving.value) return
// 重置验证错误状态
Object.keys(validationErrors).forEach(key => {
validationErrors[key] = false
@@ -1095,29 +1165,28 @@ function handleSave() {
let hasErrors = false
// 检查必填字段
// 检查必填字段,执行科室
if (!formData.executeDepartment) {
validationErrors.executeDepartment = true
hasErrors = true
}
// 检查必填字段,诊断描述
if (!formData.clinicDesc.trim()) {
if (!formData.clinicDesc?.trim()) {
validationErrors.clinicDesc = true
hasErrors = true
}
// 检查必填字段,临床诊断
if (!formData.clinicDiag.trim()) {
if (!formData.clinicDiag?.trim()) {
validationErrors.clinicDiag = true
hasErrors = true
}
// 检查必填字段,病史摘要
if (!formData.medicalHistorySummary.trim()) {
if (!formData.medicalHistorySummary?.trim()) {
validationErrors.medicalHistorySummary = true
hasErrors = true
}
// 检查必填字段,检验目的
if (!formData.purposeofInspection.trim()) {
if (!formData.purposeofInspection?.trim()) {
validationErrors.purposeofInspection = true
hasErrors = true
}
@@ -1129,7 +1198,7 @@ function handleSave() {
return
}
// 检查必填字段,申请日期
if(!formData.applyTime || (typeof formData.applyTime === 'string' && !formData.applyTime.trim())) {
if(!formData.applyTime || (typeof formData.applyTime === 'string' && !formData.applyTime?.trim())) {
validationErrors.applyTime = true
hasErrors = true
}
@@ -1153,8 +1222,6 @@ function handleSave() {
// 先检查申请单号是否已存在
console.log('查询申请单号:', formData.applyNo)
// 如果正在生成新申请单号,则跳过重复检查,直接保存
if (isGeneratingNewApplyNo.value) {
// 正在使用新生成的单号,直接保存
isGeneratingNewApplyNo.value = false; // 重置标志
@@ -1217,12 +1284,12 @@ function handleSave() {
})
}
// 执行保存操作
const executeSave = (saveData) => {
saving.value = true
saveInspectionApplication(saveData).then((res) => {
console.log('保存检验申请单结果:', res.code)
if (res.code === 200) {
ElMessage.success('保存成功')
emit('save', res.data) // 通知父组件保存成功
resetForm()
// 生成新的申请单号
generateApplicationNo().then((newApplyNo) => {
@@ -1249,35 +1316,23 @@ const executeSave = (saveData) => {
// 处理请求失败的其他错误
console.error('保存检验申请单时发生错误:', error);
ElMessage.error('保存失败,请稍后重试');
}).finally(() => {
saving.value = false
})
}
// 表单保存
function handleFormSave() {
formRef.value?.validate((valid) => {
if (valid) {
formData.labApplyItemList = selectedInspectionItems.value.map(item => item.itemId)
// console.log('保存检验申请单表单数据:', formData)
ElMessage.success('保存成功')
showForm.value = false
getInspectionList()
}
})
}
// 查看详情
function handleView(row) {
// console.log('点击查看按钮,数据:', row)
// 加载表单数据
Object.assign(formData, row)
// 根据检验项目名称找到对应的项目数据
selectedInspectionItems.value = []
const itemNames = row.inspectionItem.split('、')
const itemNames = row.itemName?.split('、') || row.inspectionItem?.split('、') || []
inspectionCategories.value.forEach(category => {
category.items.forEach(item => {
if (itemNames.includes(item.name)) {
if (itemNames.includes(item.itemName)) {
selectedInspectionItems.value.push(item)
}
})
@@ -1286,7 +1341,7 @@ function handleView(row) {
leftActiveTab.value = 'application'
}
// 切换分类
// 切换分类(修改为懒加载)
function switchCategory(category) {
if (activeCategory.value === category) {
// 如果点击的是当前激活的分类,则收起
@@ -1302,15 +1357,15 @@ function switchCategory(category) {
inspectionCategories.value.forEach(cat => {
cat.expanded = cat.key === category
})
// 懒加载该分类的项目
const targetCategory = inspectionCategories.value.find(c => c.key === category)
if (targetCategory && !targetCategory.loaded) {
loadCategoryItems(category)
}
}
}
// 处理搜索
function handleSearch() {
// 搜索逻辑已在getFilteredItems中实现这里可以添加额外的搜索逻辑
// console.log('搜索关键词:', searchKeyword.value)
}
// 处理项目项点击(排除勾选框点击)
function handleItemClick(item) {
toggleInspectionItem(item)
@@ -1462,8 +1517,8 @@ function handleCellClick(row, column) {
// 根据检验项目名称解析已选项目
selectedInspectionItems.value = []
if (row.inspectionItem) {
const itemNames = row.inspectionItem.split('、')
if (row.itemName || row.inspectionItem) {
const itemNames = (row.itemName || row.inspectionItem).split('、')
inspectionCategories.value.forEach(category => {
category.items.forEach(item => {
if (itemNames.includes(item.itemName)) {
@@ -1497,13 +1552,9 @@ watch(() => props.activeTab, async (newVal) => {
// 监听patientInfo变化,确保encounterId及时更新并重新加载数据
watch(() => props.patientInfo, async (newVal) => {
console.log('【检验】patientInfo变化:', newVal)
console.log('【检验】接收到的完整patientInfo:', JSON.stringify(newVal, null, 2))
if (newVal && newVal.encounterId) {
const oldEncounterId = queryParams.encounterId
queryParams.encounterId = newVal.encounterId
console.log('【检验】更新encounterId:', queryParams.encounterId)
// 初始化数据
await initData();
@@ -1530,9 +1581,7 @@ watch(() => selectedInspectionItems.value, (newVal) => {
// 组件挂载时预加载检验项目数据不依赖patientInfo
onMounted(async () => {
console.log('【检验】组件挂载,开始预加载检验项目数据')
await loadInspectionData()
console.log('【检验】检验项目数据预加载完成')
})
// 暴露方法
@@ -1604,6 +1653,9 @@ defineExpose({
height: 650px;
overflow-y: auto;
padding: 20px;
border: 1px solid #e4e7ed;
border-radius: 4px;
margin: 10px;
}
/* 选择区域 */
@@ -1631,6 +1683,7 @@ defineExpose({
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
}
.pagination-container {
@@ -1959,6 +2012,8 @@ defineExpose({
border: 1px solid #ebeef5;
border-radius: 4px;
background: #fafafa;
max-height: 280px;
overflow-y: auto;
}
.category-tree-item {
@@ -2009,6 +2064,39 @@ defineExpose({
border-top: 1px solid #ebeef5;
}
/* 加载中占位样式 */
.loading-placeholder {
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
color: #909399;
font-size: 14px;
}
/* 加载更多样式 */
.load-more {
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
border-top: 1px dashed #ebeef5;
}
.load-info {
margin-left: 8px;
font-size: 12px;
color: #909399;
}
/* 没有更多数据提示 */
.no-more {
text-align: center;
padding: 10px;
font-size: 12px;
color: #c0c4cc;
}
.inspection-tree-item {
display: flex;
align-items: center;
@@ -2049,10 +2137,6 @@ defineExpose({
flex-direction: column;
}
.selected-header {
flex-shrink: 0;
}
.selected-tree {
flex: 1;
}
@@ -2181,39 +2265,8 @@ defineExpose({
}
}
/* 优化表单样式 */
.application-form {
padding: 20px;
height: 700px;
overflow-y: auto;
border: 1px solid #e4e7ed;
border-radius: 4px;
margin: 10px;
}
/* 优化已选项目区样式 */
.selected-items-area .selected-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.selected-items-area .selected-tree {
max-height: 270px;
overflow-y: auto;
}
/* 优化搜索框样式 */
.inspection-selector .el-input {
margin-bottom: 15px;
}
/* 优化分类树样式 */
.category-tree {
max-height: 280px;
overflow-y: auto;
}
</style>

View File

@@ -1982,6 +1982,13 @@ function handleOrderBindInfo(bindIdInfo) {
});
list?.forEach((item) => {
// 🔧 Bug #145 修复:确保用法绑定的耗材 adviceType 为 4前端耗材类型值
// 后端返回的 adviceType=2但前端统一使用 4 表示耗材
// 必须在 setValue 之前修改,确保 setValue 方法走耗材处理逻辑
if (item.adviceType === 2) {
item.adviceType = 4;
}
rowIndex.value = prescriptionList.value.length;
setValue(item);

View File

@@ -206,3 +206,14 @@ export function getAllWards(params = {}) {
},
});
}
/**
* 获取当前登录用户有权限管理的病区
*/
export function getPractitionerWard(queryParams) {
return request({
url: '/app-common/practitioner-ward',
method: 'get',
params: queryParams,
});
}

View File

@@ -202,7 +202,7 @@
<script setup>
import {
diagnosisInit,
getAllWards,
getPractitionerWard,
getBedInfo,
getContractList,
getDiagnosisDefinitionList,
@@ -427,13 +427,13 @@ function getInitOptions() {
// 获取所有科室
const orgPromise = getOrgList();
// 获取所有病区
const wardPromise = getAllWards();
const wardPromise = getPractitionerWard();
Promise.all([orgPromise, wardPromise]).then(([orgRes, wardRes]) => {
const allOrgs = orgRes.data.records.filter(
(record) => record.typeEnum === 2 && checkClassEnumValue(record.classEnum, 2)
);
const allWards = wardRes.data.records || [];
const allWards = wardRes.data || [];
// 提取所有病区关联的科室ID
const linkedOrgIds = new Set();

View File

@@ -189,6 +189,7 @@ import {patientInfo} from '../../store/patient.js';
import {ElMessage} from 'element-plus';
// const diagnosisList = ref([]);
const allowAdd = ref(false);
const isSaving = ref(false);
const tree = ref([]);
const openDiagnosis = ref(false);
const openAddDiagnosisDialog = ref(false);
@@ -229,15 +230,42 @@ watch(
{ deep: true }
);
// 监听患者信息变化,自动获取病历详情和诊断列表
watch(
() => props.patientInfo,
(newVal) => {
if (newVal?.encounterId) {
getDetail(newVal.encounterId);
getList();
}
},
{ immediate: true, deep: true }
);
function getDetail(encounterId) {
if (!encounterId) {
console.warn('未提供有效的就诊ID无法获取病历详情');
allowAdd.value = false;
return;
}
getEmrDetail(encounterId).then((res) => {
allowAdd.value = res.data ? true : false;
});
console.log('正在获取病历详情encounterId:', encounterId);
getEmrDetail(encounterId)
.then((res) => {
console.log('病历详情API返回:', res);
if (res.code === 200) {
allowAdd.value = res.data ? true : false;
console.log('设置 allowAdd =', allowAdd.value, ', 病历数据:', res.data);
} else {
allowAdd.value = false;
console.warn('获取病历详情失败:', res.msg);
}
})
.catch((error) => {
console.error('获取病历详情异常:', error);
allowAdd.value = false;
});
}
function getList() {
@@ -245,7 +273,10 @@ function getList() {
console.warn('患者就诊信息不完整,无法获取诊断数据');
return;
}
// 初始化中医诊断列表
const newList = [];
getEncounterDiagnosis(props.patientInfo.encounterId).then((res) => {
if (res.code == 200) {
const datas = (res.data || []).map((item) => {
@@ -405,30 +436,57 @@ function getTree() {
* 添加西医诊断
*/
function handleAddDiagnosis() {
proxy.$refs.formRef.validate((valid) => {
console.log('点击新增诊断按钮allowAdd:', allowAdd.value);
// 检查表单ref是否存在
if (!proxy.$refs.formRef) {
console.error('表单ref不存在');
// 直接添加诊断,不经过表单验证
addDiagnosisItem();
return;
}
proxy.$refs.formRef.validate((valid, fields) => {
console.log('表单验证结果:', valid, '错误字段:', fields);
if (valid) {
if (!allowAdd.value) {
proxy.$modal.msgWarning('请先填写病历');
return;
}
form.value.diagnosisList.push({
showPopover: false,
name: undefined,
verificationStatusEnum: 4,
medTypeCode: undefined, // 不设默认值
diagSrtNo: form.value.diagnosisList.length + 1,
iptDiseTypeCode: 2,
diagnosisDesc: '',
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: new Date().toLocaleString('zh-CN')
});
if (form.value.diagnosisList.length == 1) {
form.value.diagnosisList[0].maindiseFlag = 1;
addDiagnosisItem();
} else {
console.warn('表单验证失败:', fields);
// 验证失败时也允许添加(因为是新增空行)
if (allowAdd.value) {
console.log('验证失败但允许添加,强制添加诊断');
addDiagnosisItem();
}
}
});
}
/**
* 添加诊断项
*/
function addDiagnosisItem() {
console.log('执行添加诊断,当前列表长度:', form.value.diagnosisList.length);
form.value.diagnosisList.push({
showPopover: false,
name: undefined,
verificationStatusEnum: 4,
medTypeCode: undefined,
diagSrtNo: form.value.diagnosisList.length + 1,
iptDiseTypeCode: 2,
diagnosisDesc: '',
diagnosisDoctor: props.patientInfo.practitionerName || props.patientInfo.doctorName || props.patientInfo.physicianName || userStore.name,
diagnosisTime: new Date().toLocaleString('zh-CN')
});
if (form.value.diagnosisList.length == 1) {
form.value.diagnosisList[0].maindiseFlag = 1;
}
console.log('添加完成,新列表长度:', form.value.diagnosisList.length);
}
// 添加中医诊断
function handleAddTcmDiagonsis() {
openAddDiagnosisDialog.value = true;

View File

@@ -541,11 +541,14 @@ function getListInfo(addNewRow) {
loadingInstance.close();
prescriptionList.value = res.data
.map((item) => {
const parsedContent = JSON.parse(item.contentJson);
return {
...JSON.parse(item.contentJson),
...parsedContent,
...item,
doseQuantity: JSON.parse(item.contentJson)?.doseQuantity,
doseUnitCode_dictText: JSON.parse(item.contentJson)?.doseUnitCode_dictText,
doseQuantity: parsedContent?.doseQuantity,
doseUnitCode_dictText: parsedContent?.doseUnitCode_dictText,
// 确保 therapyEnum 被正确设置,优先使用 contentJson 中的值
therapyEnum: String(parsedContent?.therapyEnum ?? item.therapyEnum ?? '1'),
};
})
.sort((a, b) => {
@@ -604,7 +607,7 @@ function handleAddPrescription() {
showPopover: false,
isEdit: true,
statusEnum: 1,
therapyEnum: '1',
therapyEnum: '1', // 默认为长期医嘱
});
getGroupMarkers();
nextTick(() => {
@@ -641,8 +644,8 @@ function clickRowDb(row, column, event) {
}
row.showPopover = false;
if (row.statusEnum == 1) {
// 确保治疗类型为字符串,方便与单选框 label 对齐
row.therapyEnum = String(row.therapyEnum ?? '');
// 确保治疗类型为字符串,方便与单选框 label 对齐,默认为长期医嘱('1')
row.therapyEnum = String(row.therapyEnum ?? '1');
row.isEdit = true;
const index = prescriptionList.value.findIndex((item) => item.uniqueKey === row.uniqueKey);
prescriptionList.value[index] = row;
@@ -879,13 +882,16 @@ function handleSave() {
// 此处签发处方和单行保存处方传参相同后台已经将传参存为JSON字符串此处直接转换为JSON即可
loading.value = true;
let list = saveList.map((item) => {
const parsedContent = JSON.parse(item.contentJson);
return {
...JSON.parse(item.contentJson),
...parsedContent,
adviceType: item.adviceType,
requestId: item.requestId,
dbOpType: '1',
groupId: item.groupId,
uniqueKey: undefined,
// 确保 therapyEnum 被正确传递
therapyEnum: parsedContent.therapyEnum || item.therapyEnum || '1',
};
});
// 保存签发按钮
@@ -1059,6 +1065,8 @@ function handleSaveSign(row, index) {
row.conditionDefinitionId = conditionDefinitionId.value;
row.encounterDiagnosisId = encounterDiagnosisId.value;
row.diagnosisName = diagnosisName.value;
// 确保 therapyEnum 被正确设置,默认为长期医嘱('1')
row.therapyEnum = row.therapyEnum || '1';
if (row.injectFlag == 1) {
row.sortNumber = row.sortNumber ? row.sortNumber : prescriptionList.value.length;
}
@@ -1098,8 +1106,11 @@ function handleSaveBatch() {
return item.statusEnum == 1 && !item.requestId;
})
.map((item) => {
// 确保 therapyEnum 被正确传递,默认为长期医嘱('1')
const therapyEnum = item.therapyEnum || '1';
return {
...item,
therapyEnum: therapyEnum,
dbOpType: item.requestId ? '2' : '1',
};
});
@@ -1246,6 +1257,8 @@ function handleSaveGroup(orderGroupList) {
conditionId: conditionId.value, // 诊断id
conditionDefinitionId: conditionDefinitionId.value, // 诊断定义id
encounterDiagnosisId: encounterDiagnosisId.value, // 就诊诊断id
// 确保 therapyEnum 被正确设置,默认为长期医嘱('1')
therapyEnum: prescriptionList.value[rowIndex.value]?.therapyEnum || '1',
};
// 计算价格和总量
@@ -1278,7 +1291,12 @@ function handleSaveHistory(value) {
conditionId: conditionId.value,
conditionDefinitionId: conditionDefinitionId.value,
encounterDiagnosisId: encounterDiagnosisId.value,
contentJson: JSON.stringify(value),
// 确保 therapyEnum 被正确传递,默认为长期医嘱('1')
therapyEnum: value.therapyEnum || '1',
contentJson: JSON.stringify({
...value,
therapyEnum: value.therapyEnum || '1',
}),
};
savePrescription({ adviceSaveList: [saveRow], regAdviceSaveList: [saveRow] }).then((res) => {
if (res.code === 200) {

View File

@@ -137,6 +137,7 @@ const queryParams = ref({
name: '',
useRanges: [1, 2], // 0 暂不使用 1 全院 2 科室 3 个人
organizationId: userStore.orgId,
primaryMenuEnum: 1, // 1-住院病历 (DocTypeEnum.IN_DOC)
});
const loading = ref(false); // 数据加载状态
const currentSelectTemplate = ref({
@@ -560,12 +561,12 @@ const loadLatestMedicalRecord = async () => {
loading.value = true;
try {
// 获取患者的历史病历记录
// const res = await getRecordByEncounterIdList({
// isPage: 0,
// encounterId: patientInfo.value.encounterId,
// patientId: patientInfo.value.patientId,
// definitionId: currentSelectTemplate.value.id,
// });
const res = await getRecordByEncounterIdList({
isPage: 0,
encounterId: patientInfo.value.encounterId,
patientId: patientInfo.value.patientId,
definitionId: currentSelectTemplate.value.id,
});
const historyRecords = res.data || [];
if (historyRecords.length > 0) {
@@ -623,7 +624,8 @@ const loadLatestMedicalRecord = async () => {
loading.value = false;
}
} catch (error) {
ElMessage.error('加载最新病历数据失败=====>', error);
console.error('加载最新病历数据失败:', error);
ElMessage.error('加载最新病历数据失败');
// 出错时也清空选中状态
selectedHistoryRecordId.value = '';
// 出错时也要清空表单数据,避免显示之前患者的数据

View File

@@ -153,6 +153,7 @@ const handleItemClick = (node) => {
updateLocalPatientInfo(node);
diagnosisRef.value?.getList();
diagnosisRef.value?.getDetail(node?.encounterId);
adviceRef.value?.getListInfo();
adviceRef.value?.getDiagnosisInfo();
}, 100); // 100ms 防抖延迟
@@ -189,6 +190,7 @@ watch(activeTabName, (newTab) => {
provide('diagnosisInit', (value) => {
currentPatientInfo.value = value;
diagnosisRef.value.getList();
diagnosisRef.value.getDetail(value?.encounterId);
});
provide('getAdviceList', (value) => {
adviceRef.value.getListInfo();

View File

@@ -107,6 +107,7 @@ import PatientList from '../components/patientList.vue';
import PrescriptionList from './components/prescriptionList.vue';
import SummaryMedicineList from './components/summaryMedicineList.vue';
import {inpatientNurseNavs} from '../constants/navigation';
import { RequestStatus } from '@/utils/medicalConstants';
const { proxy } = getCurrentInstance();
const router = useRouter();
@@ -115,7 +116,7 @@ const activeName = ref('preparation');
const active = ref('first');
const exeStatus = ref(1);
const deadline = ref(proxy.formatDateStr(new Date(), 'YYYY-MM-DD') + ' 23:59:59');
const requestStatus = ref(3);
const requestStatus = ref(RequestStatus.COMPLETED);
const chooseAll = ref(false);
const drugType = ref('1');
const isDetails = ref('1');

View File

@@ -26,14 +26,12 @@
>
</el-radio-group>
</el-form-item>
<el-form-item label="" prop="checkboxGroup2">
<el-form-item label="" prop="nursingLevelList">
<el-checkbox-group v-model="queryParams.nursingLevelList" @change="getList">
<el-checkbox-button
v-for="item in levelList"
:key="item.value"
v-model="item.value"
style="display: inline-block"
:label="item.label"
:label="item.value"
>
{{ item.label }}
</el-checkbox-button>
@@ -187,7 +185,7 @@ const data = reactive({
pageNo: 1,
pageSize: 10,
searchKey: undefined, // 品名/商品名/英文品名/编码/拼音
checkboxGroup2: [], // 类型(包括 1中药2成药
nursingLevelList: [], // 护理级别列表(多选
statusEnum: 1, // 状态
},
rules: {},

View File

@@ -89,7 +89,7 @@ import Filter from '@/components/TableLayout/Filter.vue';
import {computed, onBeforeMount, onMounted, reactive, ref} from 'vue';
import TransferInDialog from './transferInDialog.vue';
import SignEntryDialog from './signEntryDialog.vue';
import {childLocationList, getBedInfo, getInit, getPendingInfo, getWardList} from './api';
import {childLocationList, getBedInfo, getInit, getPendingInfo, getPractitionerWard} from './api';
import {ElLoading, ElMessage, ElMessageBox} from 'element-plus';
import PendingPatientList from '@/components/PendingPatientList/index.vue';
@@ -233,8 +233,8 @@ const ininData = async () => {
priorityOptions.value = initRes.data.priorityOptions || [];
// 然后获取病区数据(使用与病区管理页面相同的接口)
const wardRes = await getWardList({ pageNum: 1, pageSize: 50, formEnum: 4 });
const wardList = wardRes.data?.records || [];
const wardRes = await getPractitionerWard();
const wardList = wardRes.data || [];
selectHosLoding.value = false;
queryParams.value.wardId = wardList[0]?.id || '';
initInfoOptions.value.wardListOptions = wardList;

View File

@@ -389,43 +389,58 @@ const interventionForm = ref({
startTime: '', //入院时间
});
watch(
() => props.pendingInfo,
(newVal, oldVal) => {
console.log(newVal);
if (newVal) {
getPatientInfo({ encounterId: newVal.encounterId }).then((res) => {
console.log('res============>', JSON.stringify(res.data));
/**
* 获取患者详细信息并填充表单
*/
const loadPatientInfo = () => {
if (!props.pendingInfo?.encounterId) {
return;
}
getPatientInfo({ encounterId: props.pendingInfo.encounterId }).then((res) => {
pendingInfo.value = res.data;
// 从后端获取数据后设置医生和护士 ID
if (res.data.admittingDoctorId) {
interventionForm.value.admittingDoctorId = res.data.admittingDoctorId;
}
if (res.data.attendingDoctorId) {
interventionForm.value.attendingDoctorId = res.data.attendingDoctorId;
}
if (res.data.chiefDoctorId) {
interventionForm.value.chiefDoctorId = res.data.chiefDoctorId;
}
if (res.data.primaryNurseId) {
interventionForm.value.primaryNurseId = res.data.primaryNurseId;
}
if (res.data.startTime) {
interventionForm.value.startTime = dayjs(res.data.startTime).format(
'YYYY-MM-DD HH:mm:ss'
);
} else {
interventionForm.value.startTime = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss');
}
interventionForm.value.height = res.data.height;
interventionForm.value.weight = res.data.weight;
interventionForm.value.temperature = res.data.temperature;
interventionForm.value.hertRate = res.data.hertRate;
interventionForm.value.pulse = res.data.pulse;
interventionForm.value.endBloodPressure = res.data.endBloodPressure;
interventionForm.value.highBloodPressure = res.data.highBloodPressure;
});
interventionForm.value.priorityEnum = props.pendingInfo.priorityEnum || '';
interventionForm.value.organizationName = props.pendingInfo.organizationName || '';
interventionForm.value.wardName = props.pendingInfo.wardName || '';
interventionForm.value.bedName = props.pendingInfo.bedName || '';
};
pendingInfo.value = res.data;
interventionForm.value.admittingDoctorId = res.data.admittingDoctorId;
interventionForm.value.attendingDoctorId = res.data.attendingDoctorId;
if (res.data.chiefDoctorId) {
interventionForm.value.chiefDoctorId = res.data.chiefDoctorId;
}
interventionForm.value.primaryNurseId = res.data.primaryNurseId;
if (res.data.startTime) {
interventionForm.value.startTime = dayjs(res.data.startTime).format(
'YYYY-MM-DD HH:mm:ss'
);
} else {
interventionForm.value.startTime = dayjs(new Date()).format('YYYY-MM-DD HH:mm:ss');
}
interventionForm.value.height = res.data.height;
interventionForm.value.weight = res.data.weight;
interventionForm.value.temperature = res.data.temperature;
interventionForm.value.hertRate = res.data.hertRate;
interventionForm.value.pulse = res.data.pulse;
interventionForm.value.endBloodPressure = res.data.endBloodPressure;
interventionForm.value.highBloodPressure = res.data.highBloodPressure;
});
interventionForm.value.priorityEnum = newVal.priorityEnum;
interventionForm.value.organizationName = newVal.organizationName;
interventionForm.value.wardName = newVal.wardName;
interventionForm.value.bedName = newVal.bedName;
watch(
() => props.pendingInfo?.encounterId,
(newVal, oldVal) => {
// 只在 encounterId 存在且发生变化时才获取数据
if (newVal && newVal !== oldVal) {
loadPatientInfo();
}
},
{ deep: true }
{ immediate: true }
);
/* 初始化数据 */
@@ -458,8 +473,6 @@ const init = () => {
// 主任医生
getDoctorInfo({ organizationId: props.pendingInfo.organizationId })
.then((res) => {
console.log('doctorInfoOptions======>', JSON.stringify(res.data));
doctorInfoOptions.value = res.data.records || [];
nextTick(() => {
// 如果存在主任医师显示主任,如果没有选择第一个展示
@@ -498,6 +511,7 @@ const rules = reactive<FormRules>({
admittingDoctorId: [{ required: true, message: '请选择住院医生', trigger: ['blur', 'change'] }],
primaryNurseId: [{ required: true, message: '请选择责任护士', trigger: ['blur', 'change'] }],
bedLocationId: [{ required: true, message: '请选择入住床位', trigger: ['blur', 'change'] }],
// 主治医生和主任医生为可选字段,不添加 required 验证
});
const printWristband = ref(false);
@@ -512,29 +526,10 @@ const cancelAct = () => {
};
const resetForm = () => {
// interventionForm.value = {
// height: undefined,
// weight: undefined,
// temperature: undefined,
// hertRate: undefined,
// pulse: undefined,
// endBloodPressure: undefined,
// highBloodPressure: undefined,
// bedLocationId: '', // 床号
// admittingDoctorId: '', // 住院医师
// attendingDoctorId: '', // 主治医师
// chiefDoctorId: '', // 主任医师
// primaryNurseId: '', // 责任护士
// priorityEnum: '', //患者病情
// organizationName: '',
// wardName: '',
// attendingDocUpdateId: '',
// startTime: '', //入院时间
// }
// 可选:清空校验状态
// 只重置表单验证状态,不清空数据
// 数据会在下次打开对话框时通过 loadPatientInfo 重新加载
if (interventionFormRef.value) {
interventionFormRef.value.resetFields();
interventionFormRef.value.clearValidate();
}
};
@@ -583,19 +578,14 @@ const handleSubmit = async () => {
});
}
} catch (error) {
console.log('表单验证失败:', error);
console.error('表单验证失败:', error);
}
};
const openAct = () => {
init();
if (props.pendingInfo) {
interventionForm.value.priorityEnum = props.pendingInfo.priorityEnum || '';
interventionForm.value.admittingDoctorId = props.pendingInfo.practitionerId || '';
interventionForm.value.organizationName = props.pendingInfo.organizationName || '';
interventionForm.value.wardName = props.pendingInfo.wardName || '';
interventionForm.value.attendingDocUpdateId = props.pendingInfo.admittingDoctorId || '';
}
// 重新加载患者详细信息(包括医生、护士等)
loadPatientInfo();
};
const closedAct = () => {

View File

@@ -54,11 +54,12 @@
import {getCurrentInstance} from 'vue';
import PatientList from '../components/patientList.vue';
import PrescriptionList from './components/prescriptionList.vue';
import { RequestStatus } from '@/utils/medicalConstants';
const activeName = ref('preparation');
const active = ref('first');
const exeStatus = ref(1);
const requestStatus = ref(3);
const requestStatus = ref(RequestStatus.COMPLETED);
const { proxy } = getCurrentInstance();
// 存储子组件引用的对象

View File

@@ -52,10 +52,11 @@
<script setup>
import PatientList from '../components/patientList.vue';
import PrescriptionList from './components/prescriptionList.vue';
import { RequestStatus } from '@/utils/medicalConstants';
const activeName = ref('unverified');
const active = ref('first');
const requestStatus = ref(2);
const requestStatus = ref(RequestStatus.ACTIVE);
// 存储子组件引用的对象
const prescriptionRefs = ref({});

View File

@@ -136,7 +136,7 @@ const queryParams = ref({
pageNo: 1,
pageSize: 10,
searchKey: undefined,
orgId: userStore.orgId,
orgId: userStore.orgId, // 按当前用户科室过滤
});
const recordQueryParams = ref({

File diff suppressed because it is too large Load Diff

View File

@@ -516,6 +516,7 @@ import {
getPharmacyList,
getTransferProductDetail,
getTransferProductDetails,
getWarehouseList,
productTransferApproved,
reject,
submitApproval,
@@ -775,6 +776,17 @@ function handleChangePurposeTypeEnum(value, type) {
}).catch(error => {
console.error('Error fetching dispensary list:', error);
});
} else if (value == 17) {
// 耗材库类型
getWarehouseList().then((res) => {
purposeTypeListOptions.value = res.data;
if (!route.query.supplyBusNo && !type) {
receiptHeaderForm.purposeLocationId = '';
receiptHeaderForm.purposeLocationId1 = '';
}
}).catch(error => {
console.error('Error fetching warehouse list:', error);
});
} else {
purposeTypeListOptions.value = [];
}
@@ -801,6 +813,17 @@ function handleChangeSourceTypeEnum(value, type) {
}).catch(error => {
console.error('Error fetching dispensary list:', error);
});
} else if (value == 17) {
// 耗材库类型
getWarehouseList().then((res) => {
sourceTypeListOptions.value = res.data;
if (!route.query.supplyBusNo && !type) {
receiptHeaderForm.sourceLocationId = '';
receiptHeaderForm.sourceLocationId1 = '';
}
}).catch(error => {
console.error('Error fetching warehouse list:', error);
});
} else {
sourceTypeListOptions.value = [];
}

View File

@@ -123,6 +123,14 @@ export function getDispensaryList() {
method: 'get',
})
}
// 获取耗材库列表
export function getWarehouseList() {
return request({
url: '/app-common/warehouse-list',
method: 'get',
})
}
// 获取仓库药房列表
export function getpharmacyCabinetList() {
return request({

View File

@@ -616,6 +616,7 @@ import {
getInit,
getPharmacyList,
getTransferProductDetail,
getWarehouseList,
productTransferApproved,
reject,
submitApproval,
@@ -1148,6 +1149,15 @@ function handleChangePurposeTypeEnum(value, type) {
receiptHeaderForm.purposeLocationId1 = '';
}
});
} else if (value == 17) {
// 耗材库类型
getWarehouseList().then((res) => {
purposeTypeListOptions.value = res.data;
if (!route.query.supplyBusNo && !type) {
receiptHeaderForm.purposeLocationId = '';
receiptHeaderForm.purposeLocationId1 = '';
}
});
}
}
@@ -1169,6 +1179,15 @@ function handleChangeSourceTypeEnum(value, type) {
receiptHeaderForm.sourceLocationId1 = '';
}
});
} else if (value == 17) {
// 耗材库类型
getWarehouseList().then((res) => {
sourceTypeListOptions.value = res.data;
if (!route.query.supplyBusNo && !type) {
receiptHeaderForm.sourceLocationId = '';
receiptHeaderForm.sourceLocationId1 = '';
}
});
}
}

View File

@@ -239,7 +239,7 @@
<el-table-column type="selection" width="55" align="center" fixed="left"/>
<el-table-column prop="prescriptionNo" label="处方号" width="120" align="center"/>
<el-table-column prop="conditionName" label="诊断" width="120" align="center"/>
<el-table-column prop="medTypeCode_dictText" label="处方类型" width="120" align="center"/>
<el-table-column prop="dispenseEnum_enumText" label="处方类型" width="120" align="center"/>
<el-table-column prop="itemName" label="项目名称" width="160" align="center"/>
<el-table-column prop="merchandiseName" label="商品名称" width="160" align="center"/>
<el-table-column prop="quantity" label="发药数量" width="130" align="center">
@@ -836,7 +836,7 @@ function getRowMarkers(groupCounts, data) {
function spanMethod({row, column, rowIndex, columnIndex}) {
// 定义需要合并的列范围前6列包括selection列
const columnsToMerge = [1, 2, 3, 17]; // 假设selection列是第0列其他列依次是1, 2, 3, 4, 5
const columnsToMerge = [1, 2, 3]; // 假设selection列是第0列其他列依次是1, 2, 3, 4, 5
// 检查当前列是否在需要合并的列范围内
if (row.prescriptionNo) {
if (columnsToMerge.includes(columnIndex)) {

View File

@@ -0,0 +1,62 @@
-- =============================================
-- 创建 lab_observation 表
-- 用途: 实验室观察记录表
-- 创建时间: 2026-03-09
-- =============================================
-- 删除已存在的表(如果存在)
DROP TABLE IF EXISTS lab_observation;
-- 创建序列(如果不存在)
DROP SEQUENCE IF EXISTS lab_observation_id_seq;
CREATE SEQUENCE lab_observation_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
-- 创建表
CREATE TABLE lab_observation (
id BIGINT PRIMARY KEY DEFAULT nextval('lab_observation_id_seq'),
patient_id BIGINT,
encounter_id BIGINT,
specimen_id BIGINT,
observation_definition_id BIGINT,
observation_result TEXT,
observation_date TIMESTAMP,
technician_id BIGINT,
delete_flag VARCHAR(1) DEFAULT '0',
create_by VARCHAR(64) DEFAULT '',
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_by VARCHAR(64) DEFAULT '',
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
remark VARCHAR(500)
);
-- 添加表注释
COMMENT ON TABLE lab_observation IS '实验室观察记录表';
-- 添加字段注释
COMMENT ON COLUMN lab_observation.id IS '观察记录的唯一标识符';
COMMENT ON COLUMN lab_observation.patient_id IS '患者ID';
COMMENT ON COLUMN lab_observation.encounter_id IS '就诊ID';
COMMENT ON COLUMN lab_observation.specimen_id IS '关联的样本ID';
COMMENT ON COLUMN lab_observation.observation_definition_id IS '观察定义ID';
COMMENT ON COLUMN lab_observation.observation_result IS '观察结果,可能是文本描述或数值';
COMMENT ON COLUMN lab_observation.observation_date IS '观察的日期和时间';
COMMENT ON COLUMN lab_observation.technician_id IS '执行观察的技术员ID';
COMMENT ON COLUMN lab_observation.delete_flag IS '删除状态0-未删除1-已删除';
COMMENT ON COLUMN lab_observation.create_by IS '创建者';
COMMENT ON COLUMN lab_observation.create_time IS '创建时间';
COMMENT ON COLUMN lab_observation.update_by IS '更新者';
COMMENT ON COLUMN lab_observation.update_time IS '更新时间';
COMMENT ON COLUMN lab_observation.remark IS '备注';
-- 创建索引
CREATE INDEX idx_lab_observation_patient_id ON lab_observation(patient_id);
CREATE INDEX idx_lab_observation_specimen_id ON lab_observation(specimen_id);
CREATE INDEX idx_lab_observation_observation_date ON lab_observation(observation_date);
-- 打印完成信息
SELECT 'lab_observation 表创建成功!' AS message;

View File

@@ -0,0 +1,86 @@
-- 创建 lab_specimen 检验标本表
CREATE TABLE hisdev.lab_specimen (
id BIGINT NOT NULL,
service_id BIGINT,
tenant_id BIGINT,
specimen_definition_id BIGINT,
collection_status_enum INTEGER,
collection_date TIMESTAMP WITHOUT TIME ZONE,
received_date TIMESTAMP WITHOUT TIME ZONE,
specimen_volume VARCHAR(50),
specimen_unit VARCHAR(20),
create_by VARCHAR(50),
create_time TIMESTAMP WITHOUT TIME ZONE,
update_by VARCHAR(50),
update_time TIMESTAMP WITH TIME ZONE,
PRIMARY KEY (id)
);
COMMENT ON TABLE hisdev.lab_specimen IS '检验标本表';
COMMENT ON COLUMN hisdev.lab_specimen.id IS '主键ID';
COMMENT ON COLUMN hisdev.lab_specimen.service_id IS '服务请求ID(关联wor_service_request)';
COMMENT ON COLUMN hisdev.lab_specimen.tenant_id IS '租户ID';
COMMENT ON COLUMN hisdev.lab_specimen.specimen_definition_id IS '标本定义ID(关联adm_specimen_definition)';
COMMENT ON COLUMN hisdev.lab_specimen.collection_status_enum IS '采集状态(1:待采集 2:已采集 3:已接收)';
COMMENT ON COLUMN hisdev.lab_specimen.collection_date IS '采集时间';
COMMENT ON COLUMN hisdev.lab_specimen.received_date IS '接收时间';
COMMENT ON COLUMN hisdev.lab_specimen.specimen_volume IS '标本量';
COMMENT ON COLUMN hisdev.lab_specimen.specimen_unit IS '标本单位';
COMMENT ON COLUMN hisdev.lab_specimen.create_by IS '创建人';
COMMENT ON COLUMN hisdev.lab_specimen.create_time IS '创建时间';
COMMENT ON COLUMN hisdev.lab_specimen.update_by IS '更新人';
COMMENT ON COLUMN hisdev.lab_specimen.update_time IS '更新时间';
-- 创建 adm_specimen_definition 标本定义表
CREATE TABLE hisdev.adm_specimen_definition (
id BIGINT NOT NULL,
code VARCHAR(50),
name VARCHAR(100),
specimen_name VARCHAR(100),
delete_flag CHAR(1) DEFAULT '0',
create_by VARCHAR(50),
create_time TIMESTAMP WITHOUT TIME ZONE,
update_by VARCHAR(50),
update_time TIMESTAMP WITH TIME ZONE,
PRIMARY KEY (id)
);
COMMENT ON TABLE hisdev.adm_specimen_definition IS '标本定义表';
COMMENT ON COLUMN hisdev.adm_specimen_definition.id IS '主键ID';
COMMENT ON COLUMN hisdev.adm_specimen_definition.code IS '标本编码';
COMMENT ON COLUMN hisdev.adm_specimen_definition.name IS '标本名称';
COMMENT ON COLUMN hisdev.adm_specimen_definition.specimen_name IS '标本显示名称';
COMMENT ON COLUMN hisdev.adm_specimen_definition.delete_flag IS '删除标志(0:正常 1:删除)';
-- 创建 adm_observation_definition 检验项目定义表
CREATE TABLE hisdev.adm_observation_definition (
id BIGINT NOT NULL,
code VARCHAR(50),
name VARCHAR(100),
category VARCHAR(50),
unit VARCHAR(20),
reference_range VARCHAR(200),
normal_range VARCHAR(200),
delete_flag CHAR(1) DEFAULT '0',
create_by VARCHAR(50),
create_time TIMESTAMP WITHOUT TIME ZONE,
update_by VARCHAR(50),
update_time TIMESTAMP WITH TIME ZONE,
PRIMARY KEY (id)
);
COMMENT ON TABLE hisdev.adm_observation_definition IS '检验项目定义表';
COMMENT ON COLUMN hisdev.adm_observation_definition.id IS '主键ID';
COMMENT ON COLUMN hisdev.adm_observation_definition.code IS '项目编码';
COMMENT ON COLUMN hisdev.adm_observation_definition.name IS '项目名称';
COMMENT ON COLUMN hisdev.adm_observation_definition.category IS '项目类别';
COMMENT ON COLUMN hisdev.adm_observation_definition.unit IS '单位';
COMMENT ON COLUMN hisdev.adm_observation_definition.reference_range IS '参考范围';
COMMENT ON COLUMN hisdev.adm_observation_definition.normal_range IS '正常范围';
COMMENT ON COLUMN hisdev.adm_observation_definition.delete_flag IS '删除标志(0:正常 1:删除)';
-- 创建索引
CREATE INDEX idx_lab_specimen_service_id ON hisdev.lab_specimen(service_id);
CREATE INDEX idx_lab_specimen_specimen_def ON hisdev.lab_specimen(specimen_definition_id);
CREATE INDEX idx_adm_specimen_definition_code ON hisdev.adm_specimen_definition(code);
CREATE INDEX idx_adm_observation_definition_code ON hisdev.adm_observation_definition(code);

View File

@@ -0,0 +1,327 @@
-- ============================================
-- STORY #104: 增加报卡管理界面
-- 创建时间: 2026-03-05
-- 功能说明: 在疾病报告管理目录下增加报卡管理界面菜单
-- ============================================
-- 1. 创建疾病报告管理目录(如果不存在)
-- 注意需要根据实际情况调整parent_id这里假设是一级菜单
INSERT INTO "sys_menu" (
"menu_name",
"parent_id",
"order_num",
"path",
"component",
"query",
"route_name",
"is_frame",
"is_cache",
"menu_type",
"visible",
"status",
"perms",
"icon",
"create_by",
"create_time",
"update_by",
"update_time",
"remark"
)
SELECT
'疾病报告管理',
0, -- 一级菜单,根据实际情况调整
100,
'diseaseReport',
NULL,
NULL,
NULL,
'1',
'0',
'M', -- M=目录
'0',
'0',
NULL,
'documentation',
'admin',
NOW(),
NULL,
NULL,
'疾病报告管理目录'
WHERE NOT EXISTS (
SELECT 1 FROM "sys_menu" WHERE "menu_name" = '疾病报告管理'
);
-- 2. 创建报卡管理菜单
-- 获取疾病报告管理目录的ID
INSERT INTO "sys_menu" (
"menu_name",
"parent_id",
"order_num",
"path",
"component",
"query",
"route_name",
"is_frame",
"is_cache",
"menu_type",
"visible",
"status",
"perms",
"icon",
"create_by",
"create_time",
"update_by",
"update_time",
"remark"
)
SELECT
'报卡管理',
(SELECT "menu_id" FROM "sys_menu" WHERE "menu_name" = '疾病报告管理' LIMIT 1),
1,
'cardManagement',
'cardmanagement/index',
NULL,
'CardManagement',
'1',
'0',
'C', -- C=菜单
'0',
'0',
'card:management:list',
'form',
'admin',
NOW(),
NULL,
NULL,
'报卡管理界面'
WHERE NOT EXISTS (
SELECT 1 FROM "sys_menu" WHERE "menu_name" = '报卡管理'
);
-- 3. 创建报卡管理相关按钮权限
-- 获取报卡管理菜单的ID
INSERT INTO "sys_menu" (
"menu_name",
"parent_id",
"order_num",
"path",
"component",
"query",
"route_name",
"is_frame",
"is_cache",
"menu_type",
"visible",
"status",
"perms",
"icon",
"create_by",
"create_time",
"update_by",
"update_time",
"remark"
)
SELECT
'报卡查询',
(SELECT "menu_id" FROM "sys_menu" WHERE "menu_name" = '报卡管理' LIMIT 1),
1,
'',
NULL,
NULL,
NULL,
'1',
'0',
'F', -- F=按钮
'0',
'0',
'card:management:query',
'#',
'admin',
NOW(),
NULL,
NULL,
''
WHERE NOT EXISTS (
SELECT 1 FROM "sys_menu" WHERE "perms" = 'card:management:query'
);
INSERT INTO "sys_menu" (
"menu_name",
"parent_id",
"order_num",
"path",
"component",
"query",
"route_name",
"is_frame",
"is_cache",
"menu_type",
"visible",
"status",
"perms",
"icon",
"create_by",
"create_time",
"update_by",
"update_time",
"remark"
)
SELECT
'报卡审核',
(SELECT "menu_id" FROM "sys_menu" WHERE "menu_name" = '报卡管理' LIMIT 1),
2,
'',
NULL,
NULL,
NULL,
'1',
'0',
'F',
'0',
'0',
'card:management:audit',
'#',
'admin',
NOW(),
NULL,
NULL,
''
WHERE NOT EXISTS (
SELECT 1 FROM "sys_menu" WHERE "perms" = 'card:management:audit'
);
INSERT INTO "sys_menu" (
"menu_name",
"parent_id",
"order_num",
"path",
"component",
"query",
"route_name",
"is_frame",
"is_cache",
"menu_type",
"visible",
"status",
"perms",
"icon",
"create_by",
"create_time",
"update_by",
"update_time",
"remark"
)
SELECT
'报卡导出',
(SELECT "menu_id" FROM "sys_menu" WHERE "menu_name" = '报卡管理' LIMIT 1),
3,
'',
NULL,
NULL,
NULL,
'1',
'0',
'F',
'0',
'0',
'card:management:export',
'#',
'admin',
NOW(),
NULL,
NULL,
''
WHERE NOT EXISTS (
SELECT 1 FROM "sys_menu" WHERE "perms" = 'card:management:export'
);
-- 4. 创建数据库表(如果不存在)
-- 传染病报卡表
CREATE TABLE IF NOT EXISTS "infectious_card" (
"id" BIGINT PRIMARY KEY,
"card_no" VARCHAR(20) UNIQUE,
"visit_id" BIGINT,
"diag_id" BIGINT,
"pat_id" BIGINT,
"id_type" INTEGER,
"id_no" VARCHAR(30),
"pat_name" VARCHAR(50),
"parent_name" VARCHAR(50),
"sex" CHAR(1),
"birthday" DATE,
"age" INTEGER,
"age_unit" CHAR(1),
"workplace" VARCHAR(100),
"phone" VARCHAR(20),
"contact_phone" VARCHAR(20),
"address_prov" VARCHAR(50),
"address_city" VARCHAR(50),
"address_county" VARCHAR(50),
"address_town" VARCHAR(50),
"address_village" VARCHAR(80),
"address_house" VARCHAR(40),
"patientbelong" VARCHAR(20),
"occupation" VARCHAR(20),
"disease_code" VARCHAR(10),
"disease_name" VARCHAR(100),
"disease_subtype" VARCHAR(50),
"other_disease" VARCHAR(100),
"disease_type" CHAR(1),
"onset_date" DATE,
"diag_date" TIMESTAMP,
"death_date" DATE,
"revised_disease_name" VARCHAR(100),
"return_reason" VARCHAR(200),
"report_org" VARCHAR(100),
"report_org_phone" VARCHAR(20),
"report_doc" VARCHAR(50),
"report_date" DATE,
"status" CHAR(1) DEFAULT '0',
"fail_msg" VARCHAR(500),
"xml_content" TEXT,
"card_name_code" INTEGER,
"registration_source" INTEGER,
"dept_id" BIGINT,
"dept_name" VARCHAR(100),
"doctor_id" BIGINT,
"create_by" VARCHAR(64),
"create_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
"update_by" VARCHAR(64),
"update_time" TIMESTAMP,
"remark" VARCHAR(500)
);
-- 审核记录表
CREATE TABLE IF NOT EXISTS "infectious_audit" (
"audit_id" BIGINT PRIMARY KEY,
"card_id" BIGINT NOT NULL,
"audit_seq" INTEGER NOT NULL,
"audit_type" CHAR(1) NOT NULL,
"audit_status_from" CHAR(1) NOT NULL,
"audit_status_to" CHAR(1) NOT NULL,
"audit_time" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
"auditor_id" VARCHAR(20) NOT NULL,
"auditor_name" VARCHAR(50) NOT NULL,
"audit_opinion" TEXT,
"reason_for_return" TEXT,
"fail_reason_code" VARCHAR(20),
"fail_reason_desc" TEXT,
"is_batch" BOOLEAN DEFAULT FALSE,
"batch_size" INTEGER DEFAULT 1,
"create_by" VARCHAR(64),
"create_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
"update_by" VARCHAR(64),
"update_time" TIMESTAMP,
"remark" VARCHAR(500)
);
-- 创建索引
CREATE INDEX IF NOT EXISTS idx_infectious_card_no ON "infectious_card"("card_no");
CREATE INDEX IF NOT EXISTS idx_infectious_card_status ON "infectious_card"("status");
CREATE INDEX IF NOT EXISTS idx_infectious_card_create_time ON "infectious_card"("create_time");
CREATE INDEX IF NOT EXISTS idx_infectious_audit_card_id ON "infectious_audit"("card_id");
-- 添加迁移记录
INSERT INTO __MigrationsHistory (MigrationId, ProductVersion)
VALUES ('202603051000_card_management_menu', '1.0.0')
ON CONFLICT DO NOTHING;