feat(surgery): 增加手术室确认信息和次要手术功能
- 添加手术室确认时间和确认人字段显示 - 实现次要手术的添加、编辑和删除功能 - 增加急诊标志和植入高值耗材开关选项 - 添加手术费用和麻醉费用计算功能 - 实现手术和麻醉项目的远程搜索功能 - 增加第一助手和第二助手选择功能 - 优化医生列表加载逻辑,支持多接口获取 - 添加按钮图标提升界面体验 - 修复encounterId为空时的接口调用问题
This commit is contained in:
11
add_new_fields_to_cli_surgery.sql
Normal file
11
add_new_fields_to_cli_surgery.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
-- 添加新字段到cli_surgery表
|
||||
ALTER TABLE cli_surgery ADD COLUMN emergency_flag int2 DEFAULT 0;
|
||||
ALTER TABLE cli_surgery ADD COLUMN implant_flag int2 DEFAULT 0;
|
||||
ALTER TABLE cli_surgery ADD COLUMN operating_room_confirm_time timestamp;
|
||||
ALTER TABLE cli_surgery ADD COLUMN operating_room_confirm_user varchar(100);
|
||||
|
||||
-- 添加字段注释
|
||||
COMMENT ON COLUMN cli_surgery.emergency_flag IS '急诊标志 0-否 1-是';
|
||||
COMMENT ON COLUMN cli_surgery.implant_flag IS '植入高值耗材标志 0-否 1-是';
|
||||
COMMENT ON COLUMN cli_surgery.operating_room_confirm_time IS '手术室确认时间';
|
||||
COMMENT ON COLUMN cli_surgery.operating_room_confirm_user IS '手术室确认人';
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.openhis.web.clinicalmanage.appservice.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
@@ -8,6 +9,8 @@ import com.core.common.core.domain.entity.SysUser;
|
||||
import com.core.common.utils.MessageUtils;
|
||||
import com.core.common.utils.SecurityUtils;
|
||||
import com.core.system.service.ISysUserService;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.openhis.administration.domain.ChargeItem;
|
||||
import com.openhis.administration.domain.Encounter;
|
||||
import com.openhis.administration.domain.OperatingRoom;
|
||||
@@ -28,6 +31,8 @@ import com.openhis.common.enums.GenerateSource;
|
||||
import com.openhis.common.enums.RequestStatus;
|
||||
import com.openhis.common.enums.TherapyTimeType;
|
||||
import com.openhis.common.utils.HisQueryUtils;
|
||||
import com.openhis.common.utils.RedisKeys;
|
||||
import com.core.common.core.redis.RedisCache;
|
||||
import com.openhis.document.domain.RequestForm;
|
||||
import com.openhis.document.service.IRequestFormService;
|
||||
import com.openhis.web.clinicalmanage.appservice.ISurgeryAppService;
|
||||
@@ -42,9 +47,8 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.core.framework.datasource.DynamicDataSourceContextHolder.log;
|
||||
|
||||
@@ -90,8 +94,10 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
||||
@Resource
|
||||
private com.openhis.administration.service.IPractitionerService practitionerService;
|
||||
|
||||
@Resource
|
||||
private RedisCache redisCache;
|
||||
|
||||
/**
|
||||
* 分页查询手术列表
|
||||
*
|
||||
* @param surgeryDto 查询条件
|
||||
* @param pageNo 当前页
|
||||
@@ -115,16 +121,52 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
||||
|
||||
/**
|
||||
* 根据ID查询手术详情
|
||||
*
|
||||
*
|
||||
* @param id 手术ID
|
||||
* @return 手术详情
|
||||
*/
|
||||
@Override
|
||||
public R<SurgeryDto> getSurgeryDetail(Long id) {
|
||||
SurgeryDto surgeryDto = surgeryAppMapper.getSurgeryDetail(id);
|
||||
String cacheKey = RedisKeys.getSurgeryKey(id);
|
||||
SurgeryDto surgeryDto = redisCache.getCacheObject(cacheKey);
|
||||
|
||||
// 先从Redis缓存中获取
|
||||
if (surgeryDto != null) {
|
||||
log.info("从Redis缓存中获取手术信息 - surgeryId: {}", id);
|
||||
return R.ok(surgeryDto);
|
||||
}
|
||||
|
||||
// 缓存中没有,从数据库查询
|
||||
surgeryDto = surgeryAppMapper.getSurgeryDetail(id);
|
||||
if (surgeryDto == null) {
|
||||
return R.fail("手术信息不存在");
|
||||
}
|
||||
|
||||
// 从申请单中获取次要手术信息
|
||||
if (surgeryDto.getSurgeryNo() != null) {
|
||||
LambdaQueryWrapper<RequestForm> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(RequestForm::getPrescriptionNo, surgeryDto.getSurgeryNo());
|
||||
RequestForm requestForm = requestFormService.getOne(queryWrapper);
|
||||
if (requestForm != null && requestForm.getDescJson() != null) {
|
||||
try {
|
||||
Map<String, Object> map = new ObjectMapper().readValue(requestForm.getDescJson(), Map.class);
|
||||
if (map.containsKey("secondarySurgeries")) {
|
||||
surgeryDto.setSecondarySurgeries((List<Map<String, Object>>) map.get("secondarySurgeries"));
|
||||
}
|
||||
// 增加手术指征的回显兜底(从JSON中加载)
|
||||
if (map.containsKey("surgeryIndication") && (surgeryDto.getSurgeryIndication() == null || surgeryDto.getSurgeryIndication().isEmpty())) {
|
||||
surgeryDto.setSurgeryIndication((String) map.get("surgeryIndication"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("解析手术申请单JSON失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将查询结果存入Redis缓存(缓存30分钟)
|
||||
redisCache.setCacheObject(cacheKey, surgeryDto, 30, java.util.concurrent.TimeUnit.MINUTES);
|
||||
log.info("从数据库查询手术信息并存入Redis缓存 - surgeryId: {}", id);
|
||||
|
||||
return R.ok(surgeryDto);
|
||||
}
|
||||
|
||||
@@ -287,6 +329,9 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
||||
chargeItem.setTotalPrice(surgeryDto.getTotalFee() != null ? surgeryDto.getTotalFee() : new BigDecimal("0.0")); // 总价
|
||||
chargeItemService.save(chargeItem);
|
||||
|
||||
// 清除相关缓存
|
||||
clearSurgeryAppCache(surgery);
|
||||
|
||||
return R.ok(surgeryId, MessageUtils.createMessage(PromptMsgConstant.Common.M00001, new Object[]{"手术信息"}));
|
||||
}
|
||||
|
||||
@@ -297,13 +342,24 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
||||
* @return JSON字符串
|
||||
*/
|
||||
private String buildDescJson(SurgeryDto surgeryDto) {
|
||||
return String.format(
|
||||
"{\"surgeryName\":\"%s\",\"surgeryLevel\":\"%s\",\"surgeryIndication\":\"%s\",\"preoperativeDiagnosis\":\"%s\"}",
|
||||
surgeryDto.getSurgeryName() != null ? surgeryDto.getSurgeryName() : "",
|
||||
surgeryDto.getSurgeryLevel() != null ? surgeryDto.getSurgeryLevel() : "",
|
||||
surgeryDto.getSurgeryIndication() != null ? surgeryDto.getSurgeryIndication() : "",
|
||||
surgeryDto.getPreoperativeDiagnosis() != null ? surgeryDto.getPreoperativeDiagnosis() : ""
|
||||
);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("surgeryName", surgeryDto.getSurgeryName() != null ? surgeryDto.getSurgeryName() : "");
|
||||
map.put("surgeryCode", surgeryDto.getSurgeryCode() != null ? surgeryDto.getSurgeryCode() : "");
|
||||
map.put("surgeryLevel", surgeryDto.getSurgeryLevel() != null ? surgeryDto.getSurgeryLevel() : "");
|
||||
map.put("surgeryIndication", surgeryDto.getSurgeryIndication() != null ? surgeryDto.getSurgeryIndication() : "");
|
||||
map.put("preoperativeDiagnosis", surgeryDto.getPreoperativeDiagnosis() != null ? surgeryDto.getPreoperativeDiagnosis() : "");
|
||||
|
||||
// 加入次要手术信息
|
||||
if (surgeryDto.getSecondarySurgeries() != null && !surgeryDto.getSecondarySurgeries().isEmpty()) {
|
||||
map.put("secondarySurgeries", surgeryDto.getSecondarySurgeries());
|
||||
}
|
||||
|
||||
try {
|
||||
return new ObjectMapper().writeValueAsString(map);
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("构建手术申请单JSON失败", e);
|
||||
return "{}";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -340,6 +396,21 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
||||
fillSurgeryNameFields(surgery);
|
||||
|
||||
surgeryService.updateSurgery(surgery);
|
||||
|
||||
// 同步更新申请单中的描述内容
|
||||
if (surgery.getSurgeryNo() != null) {
|
||||
LambdaQueryWrapper<RequestForm> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(RequestForm::getPrescriptionNo, surgery.getSurgeryNo());
|
||||
RequestForm requestForm = requestFormService.getOne(queryWrapper);
|
||||
if (requestForm != null) {
|
||||
requestForm.setDescJson(buildDescJson(surgeryDto));
|
||||
requestFormService.updateById(requestForm);
|
||||
}
|
||||
}
|
||||
|
||||
// 清除相关缓存
|
||||
clearSurgeryAppCache(surgery);
|
||||
|
||||
return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00002, new Object[]{"手术信息"}));
|
||||
}
|
||||
|
||||
@@ -363,12 +434,16 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
||||
}
|
||||
|
||||
surgeryService.deleteSurgery(id);
|
||||
|
||||
// 清除相关缓存
|
||||
clearSurgeryAppCache(existSurgery);
|
||||
|
||||
return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00005, new Object[]{"手术信息"}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新手术状态
|
||||
*
|
||||
*
|
||||
* @param id 手术ID
|
||||
* @param statusEnum 状态
|
||||
* @return 结果
|
||||
@@ -382,6 +457,10 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
||||
}
|
||||
|
||||
surgeryService.updateSurgeryStatus(id, statusEnum);
|
||||
|
||||
// 清除相关缓存
|
||||
clearSurgeryAppCache(existSurgery);
|
||||
|
||||
return R.ok(null, MessageUtils.createMessage(PromptMsgConstant.Common.M00004, new Object[]{"手术状态"}));
|
||||
}
|
||||
|
||||
@@ -504,4 +583,32 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
||||
surgery.getPatientName(), surgery.getMainSurgeonName(), surgery.getAnesthetistName(), surgery.getAssistant1Name(),
|
||||
surgery.getAssistant2Name(), surgery.getScrubNurseName(), surgery.getOperatingRoomName(), surgery.getOrgName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除手术相关的Redis缓存
|
||||
*
|
||||
* @param surgery 手术信息
|
||||
*/
|
||||
private void clearSurgeryAppCache(Surgery surgery) {
|
||||
// 清除单个手术缓存
|
||||
if (surgery.getId() != null) {
|
||||
String surgeryKey = RedisKeys.getSurgeryKey(surgery.getId());
|
||||
redisCache.deleteObject(surgeryKey);
|
||||
log.info("清除手术缓存 - surgeryId: {}", surgery.getId());
|
||||
}
|
||||
|
||||
// 清除患者手术列表缓存
|
||||
if (surgery.getPatientId() != null) {
|
||||
String patientKey = RedisKeys.getSurgeryListByPatientKey(surgery.getPatientId());
|
||||
redisCache.deleteObject(patientKey);
|
||||
log.info("清除患者手术列表缓存 - patientId: {}", surgery.getPatientId());
|
||||
}
|
||||
|
||||
// 清除就诊手术列表缓存
|
||||
if (surgery.getEncounterId() != null) {
|
||||
String encounterKey = RedisKeys.getSurgeryListByEncounterKey(surgery.getEncounterId());
|
||||
redisCache.deleteObject(encounterKey);
|
||||
log.info("清除就诊手术列表缓存 - encounterId: {}", surgery.getEncounterId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import lombok.experimental.Accessors;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 手术管理DTO
|
||||
@@ -196,4 +198,19 @@ public class SurgeryDto {
|
||||
|
||||
/** 更新时间 */
|
||||
private Date updateTime;
|
||||
}
|
||||
|
||||
/** 急诊标志 */
|
||||
private Integer emergencyFlag;
|
||||
|
||||
/** 植入高值耗材标志 */
|
||||
private Integer implantFlag;
|
||||
|
||||
/** 手术室确认时间 */
|
||||
private Date operatingRoomConfirmTime;
|
||||
|
||||
/** 手术室确认人 */
|
||||
private String operatingRoomConfirmUser;
|
||||
|
||||
/** 次要手术列表 */
|
||||
private List<Map<String, Object>> secondarySurgeries;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="operatingRoomName" column="operating_room_name" />
|
||||
<result property="orgId" column="org_id" />
|
||||
<result property="orgName" column="org_name" />
|
||||
<result property="surgeryIndication" column="surgery_indication" />
|
||||
<result property="preoperativeDiagnosis" column="preoperative_diagnosis" />
|
||||
<result property="postoperativeDiagnosis" column="postoperative_diagnosis" />
|
||||
<result property="surgeryDescription" column="surgery_description" />
|
||||
@@ -50,6 +51,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="deleteFlag" column="delete_flag" />
|
||||
<result property="emergencyFlag" column="emergency_flag" />
|
||||
<result property="implantFlag" column="implant_flag" />
|
||||
<result property="operatingRoomConfirmTime" column="operating_room_confirm_time" />
|
||||
<result property="operatingRoomConfirmUser" column="operating_room_confirm_user" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectSurgeryVo">
|
||||
@@ -59,9 +64,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
main_surgeon_id, main_surgeon_name, assistant_1_id, assistant_1_name, assistant_2_id, assistant_2_name,
|
||||
anesthetist_id, anesthetist_name, scrub_nurse_id, scrub_nurse_name, anesthesia_type_enum,
|
||||
body_site, incision_level, healing_level, operating_room_id, operating_room_name,
|
||||
org_id, org_name, preoperative_diagnosis, postoperative_diagnosis, surgery_description,
|
||||
org_id, org_name, surgery_indication, preoperative_diagnosis, postoperative_diagnosis, surgery_description,
|
||||
postoperative_advice, complications, surgery_fee, anesthesia_fee, total_fee, remark,
|
||||
create_by, create_time, update_by, update_time, delete_flag
|
||||
create_by, create_time, update_by, update_time, delete_flag,
|
||||
emergency_flag, implant_flag, operating_room_confirm_time, operating_room_confirm_user
|
||||
FROM cli_surgery
|
||||
</sql>
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="applyDoctorName" column="apply_doctor_name" />
|
||||
<result property="applyDeptId" column="apply_dept_id" />
|
||||
<result property="applyDeptName" column="apply_dept_name" />
|
||||
<result property="surgeryIndication" column="surgery_indication" />
|
||||
<result property="surgeryName" column="surgery_name" />
|
||||
<result property="surgeryCode" column="surgery_code" />
|
||||
<result property="surgeryTypeEnum" column="surgery_type_enum" />
|
||||
@@ -62,6 +63,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="remark" column="remark" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
<result property="emergencyFlag" column="emergency_flag" />
|
||||
<result property="implantFlag" column="implant_flag" />
|
||||
<result property="operatingRoomConfirmTime" column="operating_room_confirm_time" />
|
||||
<result property="operatingRoomConfirmUser" column="operating_room_confirm_user" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectSurgeryVo">
|
||||
@@ -153,6 +158,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
ro.name as operating_room_org_name,
|
||||
s.org_id,
|
||||
COALESCE(s.org_name, o.name) as org_name,
|
||||
s.surgery_indication,
|
||||
s.preoperative_diagnosis,
|
||||
s.postoperative_diagnosis,
|
||||
s.surgery_description,
|
||||
@@ -163,7 +169,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
s.total_fee,
|
||||
s.remark,
|
||||
s.create_time,
|
||||
s.update_time
|
||||
s.update_time,
|
||||
s.emergency_flag,
|
||||
s.implant_flag,
|
||||
s.operating_room_confirm_time,
|
||||
s.operating_room_confirm_user
|
||||
FROM cli_surgery s
|
||||
LEFT JOIN adm_patient p ON s.patient_id = p.id
|
||||
LEFT JOIN adm_encounter e ON s.encounter_id = e.id
|
||||
|
||||
@@ -22,4 +22,31 @@ public class RedisKeys {
|
||||
public static String getProductsKey(String itemId){
|
||||
return "products_change_price:item_" + itemId + "_key";
|
||||
}
|
||||
|
||||
/**
|
||||
* 手术信息缓存
|
||||
* @param surgeryId 手术ID
|
||||
* @return
|
||||
*/
|
||||
public static String getSurgeryKey(Long surgeryId){
|
||||
return "surgery:info:" + surgeryId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手术列表缓存(按患者ID)
|
||||
* @param patientId 患者ID
|
||||
* @return
|
||||
*/
|
||||
public static String getSurgeryListByPatientKey(Long patientId){
|
||||
return "surgery:patient:" + patientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手术列表缓存(按就诊ID)
|
||||
* @param encounterId 就诊ID
|
||||
* @return
|
||||
*/
|
||||
public static String getSurgeryListByEncounterKey(Long encounterId){
|
||||
return "surgery:encounter:" + encounterId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,4 +211,20 @@ public class Surgery extends HisBaseEntity {
|
||||
/** 备注信息 */
|
||||
@TableField("remark")
|
||||
private String remark;
|
||||
|
||||
/** 急诊标志 */
|
||||
@TableField("emergency_flag")
|
||||
private Integer emergencyFlag;
|
||||
|
||||
/** 植入高值耗材标志 */
|
||||
@TableField("implant_flag")
|
||||
private Integer implantFlag;
|
||||
|
||||
/** 手术室确认时间 */
|
||||
@TableField("operating_room_confirm_time")
|
||||
private Date operatingRoomConfirmTime;
|
||||
|
||||
/** 手术室确认人 */
|
||||
@TableField("operating_room_confirm_user")
|
||||
private String operatingRoomConfirmUser;
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.openhis.clinical.domain.Surgery;
|
||||
import com.openhis.clinical.mapper.SurgeryMapper;
|
||||
import com.openhis.clinical.service.ISurgeryService;
|
||||
import com.openhis.common.utils.RedisKeys;
|
||||
import com.core.common.core.redis.RedisCache;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -14,6 +16,7 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@@ -22,8 +25,10 @@ public class SurgeryServiceImpl extends ServiceImpl<SurgeryMapper, Surgery> impl
|
||||
@Resource
|
||||
private SurgeryMapper surgeryMapper;
|
||||
|
||||
@Resource
|
||||
private RedisCache redisCache;
|
||||
|
||||
/**
|
||||
* 新增手术信息
|
||||
*
|
||||
* @param surgery 手术信息
|
||||
* @return 手术ID
|
||||
@@ -50,6 +55,9 @@ public class SurgeryServiceImpl extends ServiceImpl<SurgeryMapper, Surgery> impl
|
||||
|
||||
surgeryMapper.insert(surgery);
|
||||
|
||||
// 清除相关缓存
|
||||
clearSurgeryCache(surgery);
|
||||
|
||||
// 插入后再查询一次,验证是否保存成功
|
||||
Surgery inserted = surgeryMapper.selectById(surgery.getId());
|
||||
log.info("插入后查询结果 - applyDoctorId: {}, applyDoctorName: {}, applyDeptId: {}, applyDeptName: {}",
|
||||
@@ -78,66 +86,96 @@ public class SurgeryServiceImpl extends ServiceImpl<SurgeryMapper, Surgery> impl
|
||||
return "OP" + dateStr + randomStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改手术信息
|
||||
*
|
||||
* @param surgery 手术信息
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updateSurgery(Surgery surgery) {
|
||||
surgery.setUpdateTime(new Date());
|
||||
return surgeryMapper.updateById(surgery) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除手术信息
|
||||
*
|
||||
* @param id 手术ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean deleteSurgery(Long id) {
|
||||
return surgeryMapper.deleteById(id) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查询手术信息
|
||||
*
|
||||
*
|
||||
* @param id 手术ID
|
||||
* @return 手术信息
|
||||
*/
|
||||
@Override
|
||||
public Surgery getSurgeryById(Long id) {
|
||||
return surgeryMapper.selectById(id);
|
||||
String cacheKey = RedisKeys.getSurgeryKey(id);
|
||||
|
||||
// 先从Redis缓存中获取
|
||||
Surgery surgery = redisCache.getCacheObject(cacheKey);
|
||||
if (surgery != null) {
|
||||
log.info("从Redis缓存中获取手术信息 - surgeryId: {}", id);
|
||||
return surgery;
|
||||
}
|
||||
|
||||
// 缓存中没有,从数据库查询
|
||||
surgery = surgeryMapper.selectById(id);
|
||||
if (surgery != null) {
|
||||
// 将查询结果存入Redis缓存(缓存30分钟)
|
||||
redisCache.setCacheObject(cacheKey, surgery, 30, TimeUnit.MINUTES);
|
||||
log.info("从数据库查询手术信息并存入Redis缓存 - surgeryId: {}", id);
|
||||
}
|
||||
|
||||
return surgery;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据患者ID查询手术列表
|
||||
*
|
||||
*
|
||||
* @param patientId 患者ID
|
||||
* @return 手术列表
|
||||
*/
|
||||
@Override
|
||||
public List<Surgery> getSurgeryListByPatientId(Long patientId) {
|
||||
String cacheKey = RedisKeys.getSurgeryListByPatientKey(patientId);
|
||||
|
||||
// 先从Redis缓存中获取
|
||||
List<Surgery> surgeryList = redisCache.getCacheObject(cacheKey);
|
||||
if (surgeryList != null) {
|
||||
log.info("从Redis缓存中获取患者手术列表 - patientId: {}", patientId);
|
||||
return surgeryList;
|
||||
}
|
||||
|
||||
// 缓存中没有,从数据库查询
|
||||
LambdaQueryWrapper<Surgery> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Surgery::getPatientId, patientId)
|
||||
.eq(Surgery::getDeleteFlag, "0")
|
||||
.orderByDesc(Surgery::getCreateTime);
|
||||
return surgeryMapper.selectList(wrapper);
|
||||
surgeryList = surgeryMapper.selectList(wrapper);
|
||||
|
||||
// 将查询结果存入Redis缓存(缓存30分钟)
|
||||
if (surgeryList != null && !surgeryList.isEmpty()) {
|
||||
redisCache.setCacheObject(cacheKey, surgeryList, 30, TimeUnit.MINUTES);
|
||||
log.info("从数据库查询患者手术列表并存入Redis缓存 - patientId: {}, count: {}", patientId, surgeryList.size());
|
||||
}
|
||||
|
||||
return surgeryList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据就诊ID查询手术列表
|
||||
*
|
||||
*
|
||||
* @param encounterId 就诊ID
|
||||
* @return 手术列表
|
||||
*/
|
||||
@Override
|
||||
public List<Surgery> getSurgeryListByEncounterId(Long encounterId) {
|
||||
return surgeryMapper.selectByEncounterId(encounterId);
|
||||
String cacheKey = RedisKeys.getSurgeryListByEncounterKey(encounterId);
|
||||
|
||||
// 先从Redis缓存中获取
|
||||
List<Surgery> surgeryList = redisCache.getCacheObject(cacheKey);
|
||||
if (surgeryList != null) {
|
||||
log.info("从Redis缓存中获取就诊手术列表 - encounterId: {}", encounterId);
|
||||
return surgeryList;
|
||||
}
|
||||
|
||||
// 缓存中没有,从数据库查询
|
||||
surgeryList = surgeryMapper.selectByEncounterId(encounterId);
|
||||
|
||||
// 将查询结果存入Redis缓存(缓存30分钟)
|
||||
if (surgeryList != null && !surgeryList.isEmpty()) {
|
||||
redisCache.setCacheObject(cacheKey, surgeryList, 30, TimeUnit.MINUTES);
|
||||
log.info("从数据库查询就诊手术列表并存入Redis缓存 - encounterId: {}, count: {}", encounterId, surgeryList.size());
|
||||
}
|
||||
|
||||
return surgeryList;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,4 +205,72 @@ public class SurgeryServiceImpl extends ServiceImpl<SurgeryMapper, Surgery> impl
|
||||
|
||||
return surgeryMapper.updateById(surgery) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改手术信息
|
||||
*
|
||||
* @param surgery 手术信息
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean updateSurgery(Surgery surgery) {
|
||||
surgery.setUpdateTime(new Date());
|
||||
boolean result = surgeryMapper.updateById(surgery) > 0;
|
||||
|
||||
if (result) {
|
||||
// 清除相关缓存
|
||||
clearSurgeryCache(surgery);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除手术信息
|
||||
*
|
||||
* @param id 手术ID
|
||||
* @return 结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean deleteSurgery(Long id) {
|
||||
Surgery surgery = surgeryMapper.selectById(id);
|
||||
boolean result = surgeryMapper.deleteById(id) > 0;
|
||||
|
||||
if (result && surgery != null) {
|
||||
// 清除相关缓存
|
||||
clearSurgeryCache(surgery);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除手术相关的Redis缓存
|
||||
*
|
||||
* @param surgery 手术信息
|
||||
*/
|
||||
private void clearSurgeryCache(Surgery surgery) {
|
||||
// 清除单个手术缓存
|
||||
if (surgery.getId() != null) {
|
||||
String surgeryKey = RedisKeys.getSurgeryKey(surgery.getId());
|
||||
redisCache.deleteObject(surgeryKey);
|
||||
log.info("清除手术缓存 - surgeryId: {}", surgery.getId());
|
||||
}
|
||||
|
||||
// 清除患者手术列表缓存
|
||||
if (surgery.getPatientId() != null) {
|
||||
String patientKey = RedisKeys.getSurgeryListByPatientKey(surgery.getPatientId());
|
||||
redisCache.deleteObject(patientKey);
|
||||
log.info("清除患者手术列表缓存 - patientId: {}", surgery.getPatientId());
|
||||
}
|
||||
|
||||
// 清除就诊手术列表缓存
|
||||
if (surgery.getEncounterId() != null) {
|
||||
String encounterKey = RedisKeys.getSurgeryListByEncounterKey(surgery.getEncounterId());
|
||||
redisCache.deleteObject(encounterKey);
|
||||
log.info("清除就诊手术列表缓存 - encounterId: {}", surgery.getEncounterId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -808,6 +808,17 @@ export function getInspectionApplicationList(queryParams) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取诊疗项目列表
|
||||
*/
|
||||
export function getDiagnosisTreatmentList(queryParams) {
|
||||
return request({
|
||||
url: '/data-dictionary/diagnosis-treatment/information-page',
|
||||
method: 'get',
|
||||
params: queryParams,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存检验申请单
|
||||
*/
|
||||
|
||||
@@ -595,13 +595,21 @@ function initData() {
|
||||
|
||||
// 获取检验申请单列表
|
||||
function getInspectionList() {
|
||||
// 如果没有encounterId,不调用接口
|
||||
if (!queryParams.encounterId) {
|
||||
console.warn('【检验】encounterId为空,不调用接口')
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
|
||||
// 调用真实的API,只传递 encounterId 参数
|
||||
console.log('【检验】调用API,encounterId:', queryParams.encounterId)
|
||||
getInspectionApplicationList({ encounterId: queryParams.encounterId }).then((res) => {
|
||||
if (res.code === 200) {
|
||||
inspectionList.value = res.data || []
|
||||
total.value = res.data?.length || 0
|
||||
console.log('【检验】获取数据成功,数量:', inspectionList.value.length)
|
||||
} else {
|
||||
inspectionList.value = []
|
||||
total.value = 0
|
||||
@@ -977,6 +985,15 @@ watch(() => props.activeTab, (newVal) => {
|
||||
}
|
||||
})
|
||||
|
||||
// 监听patientInfo变化,确保encounterId及时更新
|
||||
watch(() => props.patientInfo, (newVal) => {
|
||||
console.log('【检验】patientInfo变化:', newVal)
|
||||
if (newVal && newVal.encounterId) {
|
||||
queryParams.encounterId = newVal.encounterId
|
||||
console.log('【检验】更新encounterId:', queryParams.encounterId)
|
||||
}
|
||||
}, { deep: true, immediate: true })
|
||||
|
||||
// 初始化
|
||||
onMounted(() => {
|
||||
initData()
|
||||
|
||||
@@ -50,6 +50,16 @@
|
||||
<!-- 手术等级 -->
|
||||
<el-table-column label="手术等级" align="center" prop="surgeryLevel_dictText" width="100" />
|
||||
|
||||
<!-- 手术室确认时间 -->
|
||||
<el-table-column label="手术室确认时间" align="center" prop="operatingRoomConfirmTime" width="180">
|
||||
<template #default="scope">
|
||||
{{ scope.row.operatingRoomConfirmTime ? parseTime(scope.row.operatingRoomConfirmTime, '{y}-{m}-{d} {h}:{i}:{s}') : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 手术室确认人 -->
|
||||
<el-table-column label="手术室确认人" align="center" prop="operatingRoomConfirmUser" width="100" />
|
||||
|
||||
<!-- 状态 -->
|
||||
<el-table-column label="状态" align="center" prop="statusEnum_dictText" width="100">
|
||||
<template #default="scope">
|
||||
@@ -63,13 +73,13 @@
|
||||
<el-table-column label="操作" align="center" width="200" fixed="right">
|
||||
<template #default="scope">
|
||||
<!-- 查看:显示手术申请详情(只读模式) -->
|
||||
<el-button link type="primary" @click="handleView(scope.row)">查看</el-button>
|
||||
<el-button link type="primary" icon="View" @click="handleView(scope.row)">查看</el-button>
|
||||
|
||||
<!-- 编辑:修改手术申请信息(只有状态为新开的能修改) -->
|
||||
<el-button link type="primary" @click="handleEdit(scope.row)" v-if="scope.row.statusEnum === 0">编辑</el-button>
|
||||
<el-button link type="primary" icon="Edit" @click="handleEdit(scope.row)" v-if="scope.row.statusEnum === 0">编辑</el-button>
|
||||
|
||||
<!-- 删除:取消手术申请(作废) -->
|
||||
<el-button link type="danger" @click="handleDelete(scope.row)" v-if="scope.row.statusEnum === 0 || scope.row.statusEnum === 1">删除</el-button>
|
||||
<el-button link type="danger" icon="Delete" @click="handleDelete(scope.row)" v-if="scope.row.statusEnum === 0 || scope.row.statusEnum === 1">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -144,11 +154,27 @@
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手术名称" prop="surgeryName">
|
||||
<el-input v-model="form.surgeryName" placeholder="请选择手术名称">
|
||||
<template #append>
|
||||
<el-button icon="Search" @click="selectSurgeryName" />
|
||||
</template>
|
||||
</el-input>
|
||||
<el-select
|
||||
v-model="form.surgeryName"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="请输入关键词搜索手术"
|
||||
:remote-method="remoteSearchSurgery"
|
||||
:loading="surgeryLoading"
|
||||
style="width: 100%"
|
||||
@change="handleSurgeryChange"
|
||||
@focus="() => remoteSearchSurgery('')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in surgeryNameList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.name"
|
||||
>
|
||||
<span>{{ item.retailPrice || item.retail_price || item.salePrice || item.price || 0 }}元 - {{ item.name }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
@@ -167,6 +193,97 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="添加次要手术" prop="appendSurgery">
|
||||
<el-button type="primary" plain icon="Plus" @click="addAppendSurgery" style="width: 100%">添加次要手术</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 次要手术表格 -->
|
||||
<el-row v-if="form.secondarySurgeries && form.secondarySurgeries.length > 0">
|
||||
<el-col :span="24" style="margin-bottom: 20px;">
|
||||
<el-table :data="form.secondarySurgeries" border stripe size="small">
|
||||
<el-table-column label="手术名称" min-width="200">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
v-model="scope.row.surgeryName"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="搜索次要手术"
|
||||
:remote-method="remoteSearchSurgery"
|
||||
:loading="surgeryLoading"
|
||||
style="width: 100%"
|
||||
@change="(val) => handleSecondarySurgeryChange(val, scope.row)"
|
||||
@focus="() => remoteSearchSurgery('')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in surgeryNameList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.name"
|
||||
>
|
||||
<span>{{ item.retailPrice || item.retail_price || item.salePrice || item.price || 0 }}元 - {{ item.name }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="手术等级" width="130">
|
||||
<template #default="scope">
|
||||
<el-select v-model="scope.row.surgeryLevel" placeholder="选择等级">
|
||||
<el-option label="一级手术" :value="1" />
|
||||
<el-option label="二级手术" :value="2" />
|
||||
<el-option label="三级手术" :value="3" />
|
||||
<el-option label="四级手术" :value="4" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="切口类型" width="130">
|
||||
<template #default="scope">
|
||||
<el-select v-model="scope.row.incisionLevel" placeholder="选择切口">
|
||||
<el-option label="I类切口" :value="1" />
|
||||
<el-option label="II类切口" :value="2" />
|
||||
<el-option label="III类切口" :value="3" />
|
||||
<el-option label="IV类切口" :value="4" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="麻醉方式" width="180">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
v-model="scope.row.anesthesiaName"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="搜索麻醉"
|
||||
:remote-method="remoteSearchAnesthesia"
|
||||
:loading="anesthesiaLoading"
|
||||
style="width: 100%"
|
||||
@change="(val) => handleSecondaryAnesthesiaChange(val, scope.row)"
|
||||
@focus="() => remoteSearchAnesthesia('')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in anesthesiaNameList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.name"
|
||||
>
|
||||
<span>{{ item.retailPrice || item.retail_price || item.salePrice || item.price || 0 }}元 - {{ item.name }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="80" align="center">
|
||||
<template #default="scope">
|
||||
<el-button link type="danger" icon="Delete" @click="removeSecondarySurgery(scope.$index)"></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 医疗信息区 -->
|
||||
<el-divider content-position="left">医疗信息</el-divider>
|
||||
|
||||
@@ -195,10 +312,27 @@
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="麻醉方式" prop="anesthesiaTypeEnum">
|
||||
<el-select v-model="form.anesthesiaTypeEnum" placeholder="请选择麻醉方式" style="width: 100%">
|
||||
<el-option label="局麻" :value="1" />
|
||||
<el-option label="全麻" :value="3" />
|
||||
<el-form-item label="麻醉方式" prop="anesthesiaName">
|
||||
<el-select
|
||||
v-model="form.anesthesiaName"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="搜索麻醉项目"
|
||||
:remote-method="remoteSearchAnesthesia"
|
||||
:loading="anesthesiaLoading"
|
||||
style="width: 100%"
|
||||
@change="handleAnesthesiaChange"
|
||||
@focus="() => remoteSearchAnesthesia('')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in anesthesiaNameList"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.name"
|
||||
>
|
||||
<span>{{ item.retailPrice || item.retail_price || item.salePrice || item.price || 0 }}元 - {{ item.name }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -224,12 +358,22 @@
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手术助手" prop="assistantId">
|
||||
<el-select v-model="form.assistantId" filterable clearable placeholder="请选择手术助手" style="width: 100%">
|
||||
<el-form-item label="第一助手" prop="assistant1Id">
|
||||
<el-select v-model="form.assistant1Id" filterable clearable placeholder="请选择第一助手" style="width: 100%">
|
||||
<el-option v-for="item in doctorList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="第二助手" prop="assistant2Id">
|
||||
<el-select v-model="form.assistant2Id" filterable clearable placeholder="请选择第二助手" style="width: 100%">
|
||||
<el-option v-for="item in doctorList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="申请科室" prop="applyDeptName">
|
||||
<el-input v-model="form.applyDeptName" disabled placeholder="系统自动获取" />
|
||||
@@ -240,6 +384,31 @@
|
||||
<!-- 其他信息区 -->
|
||||
<el-divider content-position="left">其他信息</el-divider>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="急诊标志" prop="emergencyFlag">
|
||||
<el-switch
|
||||
v-model="form.emergencyFlag"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="植入高值耗材" prop="implantFlag">
|
||||
<el-switch
|
||||
v-model="form.implantFlag"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
active-text="是"
|
||||
inactive-text="否"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="术前诊断" prop="preoperativeDiagnosis">
|
||||
<el-input v-model="form.preoperativeDiagnosis" disabled placeholder="自动获取门诊诊断的主要诊断名称" type="textarea" :rows="3" />
|
||||
</el-form-item>
|
||||
@@ -247,12 +416,40 @@
|
||||
<el-form-item label="手术指征" prop="surgeryIndication">
|
||||
<el-input v-model="form.surgeryIndication" placeholder="请输入手术指征" type="textarea" :rows="3" />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 费用信息区 -->
|
||||
<el-divider content-position="left">费用信息</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手术费用">
|
||||
<el-input v-model="form.surgeryFee" disabled placeholder="0.00">
|
||||
<template #append>元</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="麻醉费用">
|
||||
<el-input v-model="form.anesthesiaFee" disabled placeholder="0.00">
|
||||
<template #append>元</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="总计费用">
|
||||
<el-input :value="totalCalculatedFee" disabled placeholder="0.00">
|
||||
<template #append>元</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
<el-button type="primary" @click="submitForm">提交申请</el-button>
|
||||
<el-button icon="Close" @click="cancel">取 消</el-button>
|
||||
<el-button type="primary" icon="Check" @click="submitForm">提交申请</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
@@ -278,15 +475,28 @@
|
||||
<el-descriptions-item label="申请科室">{{ viewData.applyDeptName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="术前诊断" :span="2">{{ viewData.preoperativeDiagnosis }}</el-descriptions-item>
|
||||
<el-descriptions-item label="手术指征" :span="2">{{ viewData.surgeryIndication }}</el-descriptions-item>
|
||||
|
||||
<!-- 次要手术详情展示 -->
|
||||
<el-descriptions-item label="次要手术" :span="2" v-if="viewData.secondarySurgeries && viewData.secondarySurgeries.length > 0">
|
||||
<el-table :data="viewData.secondarySurgeries" border size="small">
|
||||
<el-table-column prop="surgeryName" label="手术名称" />
|
||||
<el-table-column prop="surgeryCode" label="手术编码" width="120" />
|
||||
<el-table-column label="等级" width="100">
|
||||
<template #default="scope">
|
||||
{{ scope.row.surgeryLevel === 1 ? '一级' : scope.row.surgeryLevel === 2 ? '二级' : scope.row.surgeryLevel === 3 ? '三级' : scope.row.surgeryLevel === 4 ? '四级' : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="SurgeryApplication">
|
||||
import { getCurrentInstance, ref, computed, watch } from 'vue'
|
||||
import { getCurrentInstance, ref, computed, watch, onMounted } from 'vue'
|
||||
import { getSurgeryPage, addSurgery, updateSurgery, deleteSurgery, getSurgeryDetail, updateSurgeryStatus } from '@/api/surgerymanage'
|
||||
import { getEncounterDiagnosis } from '../api'
|
||||
import { getEncounterDiagnosis, getDiagnosisTreatmentList, queryParticipantList } from '../api'
|
||||
import { listUser } from '@/api/system/user'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
|
||||
@@ -320,20 +530,55 @@ const form = ref({
|
||||
applyDeptName: undefined,
|
||||
surgeryNo: undefined,
|
||||
surgeryName: undefined,
|
||||
surgeryCode: undefined,
|
||||
surgeryTypeEnum: undefined,
|
||||
surgeryLevel: undefined,
|
||||
plannedTime: undefined,
|
||||
mainSurgeonId: undefined,
|
||||
assistantId: undefined,
|
||||
assistant1Id: undefined,
|
||||
assistant2Id: undefined,
|
||||
anesthesiaTypeEnum: undefined,
|
||||
anesthesiaName: undefined,
|
||||
anesthesiaFee: undefined,
|
||||
surgeryFee: undefined,
|
||||
totalFee: undefined,
|
||||
incisionLevel: undefined,
|
||||
preoperativeDiagnosis: undefined,
|
||||
surgeryIndication: undefined
|
||||
surgeryIndication: undefined,
|
||||
emergencyFlag: 0, // 急诊标志,默认为否
|
||||
implantFlag: 0, // 植入高值耗材标志,默认为否
|
||||
secondarySurgeries: [] // 次要手术列表
|
||||
})
|
||||
const surgeryRef = ref()
|
||||
const viewData = ref({})
|
||||
const title = ref('')
|
||||
const doctorList = ref([])
|
||||
const surgeryNameList = ref([])
|
||||
const anesthesiaNameList = ref([])
|
||||
const surgeryLoading = ref(false)
|
||||
const anesthesiaLoading = ref(false)
|
||||
|
||||
// 计算总费用
|
||||
const totalCalculatedFee = computed(() => {
|
||||
const surgeryFee = parseFloat(form.value.surgeryFee || 0)
|
||||
const anesthesiaFee = parseFloat(form.value.anesthesiaFee || 0)
|
||||
// 加上次要手术费用
|
||||
let secondaryFee = 0
|
||||
if (form.value.secondarySurgeries && form.value.secondarySurgeries.length > 0) {
|
||||
secondaryFee = form.value.secondarySurgeries.reduce((acc, curr) => {
|
||||
// 累加次要手术的手术费和麻醉费
|
||||
const sFee = parseFloat(curr.retailPrice || curr.retail_price || curr.surgeryFee || 0)
|
||||
const aFee = parseFloat(curr.anesthesiaFee || 0)
|
||||
return acc + sFee + aFee
|
||||
}, 0)
|
||||
}
|
||||
return (surgeryFee + anesthesiaFee + secondaryFee).toFixed(2)
|
||||
})
|
||||
|
||||
// 监听总费用变化,同步到表单对象
|
||||
watch(totalCalculatedFee, (newVal) => {
|
||||
form.value.totalFee = newVal
|
||||
})
|
||||
|
||||
// 字典选项
|
||||
const surgeryStatusOptions = ref([
|
||||
@@ -357,7 +602,9 @@ const rules = ref({
|
||||
surgeryLevel: [{ required: true, message: '请选择手术等级', trigger: 'change' }],
|
||||
plannedTime: [{ required: true, message: '请选择计划手术时间', trigger: 'change' }],
|
||||
mainSurgeonId: [{ required: true, message: '请选择主刀医生', trigger: 'change' }],
|
||||
anesthesiaTypeEnum: [{ required: true, message: '请选择麻醉方式', trigger: 'change' }]
|
||||
anesthesiaName: [{ required: true, message: '请选择麻醉方式', trigger: 'change' }],
|
||||
applyDoctorName: [{ required: true, message: '申请医生不能为空', trigger: 'blur' }],
|
||||
applyDeptName: [{ required: true, message: '申请科室不能为空', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
// 监听患者信息变化
|
||||
@@ -369,6 +616,11 @@ watch(() => props.patientInfo, (newVal) => {
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 挂载时加载医生列表
|
||||
onMounted(() => {
|
||||
loadDoctorList()
|
||||
})
|
||||
|
||||
// 获取手术申请列表
|
||||
function getList() {
|
||||
if (!props.patientInfo?.encounterId) {
|
||||
@@ -412,18 +664,53 @@ function loadDiagnosisInfo() {
|
||||
|
||||
// 加载医生列表
|
||||
function loadDoctorList() {
|
||||
listUser({ pageNo: 1, pageSize: 1000 }).then(res => {
|
||||
if (res.code === 200) {
|
||||
doctorList.value = res.data.records || []
|
||||
console.log('加载医生列表成功,数量:', doctorList.value.length)
|
||||
} else {
|
||||
proxy.$modal.msgError('获取医生列表失败')
|
||||
doctorList.value = []
|
||||
console.log('开始加载医生列表...')
|
||||
// 同时尝试两个接口,确保能获取到数据
|
||||
const p1 = queryParticipantList({ searchKey: '' }).catch(() => ({ code: 500 }))
|
||||
const p2 = listUser({ pageNo: 1, pageSize: 1000 }).catch(() => ({ code: 500 }))
|
||||
|
||||
Promise.all([p1, p2]).then(([res1, res2]) => {
|
||||
let list = []
|
||||
|
||||
// 方案1:从参与者接口获取
|
||||
if (res1.code === 200 && res1.data && Array.isArray(res1.data) && res1.data.length > 0) {
|
||||
list = res1.data.map(item => ({
|
||||
id: item.practitionerId || item.practitioner_id || item.id,
|
||||
name: item.practitionerName || item.practitioner_name || item.nickName || item.name,
|
||||
orgName: item.organizationName || item.organization_name || item.deptName
|
||||
}))
|
||||
console.log('从参与者接口获取到医生:', list.length)
|
||||
}
|
||||
|
||||
// 方案2:如果方案1没数据,从用户接口获取
|
||||
if (list.length === 0 && res2.code === 200) {
|
||||
const data = res2.data?.records || res2.rows || res2.data || []
|
||||
if (Array.isArray(data)) {
|
||||
list = data.map(item => ({
|
||||
id: item.practitionerId || item.id || item.userId,
|
||||
name: item.nickName || item.name || item.userName,
|
||||
orgName: item.deptName || item.organizationName || item.orgId_dictText
|
||||
}))
|
||||
console.log('从用户接口获取到医生:', list.length)
|
||||
}
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('加载医生列表失败:', error)
|
||||
proxy.$modal.msgError('获取医生列表失败')
|
||||
doctorList.value = []
|
||||
|
||||
if (list.length > 0) {
|
||||
// 去重处理 (根据ID)
|
||||
const uniqueList = []
|
||||
const idMap = new Set()
|
||||
for (const item of list) {
|
||||
if (item.id && !idMap.has(String(item.id))) {
|
||||
idMap.add(String(item.id))
|
||||
uniqueList.push(item)
|
||||
}
|
||||
}
|
||||
doctorList.value = uniqueList
|
||||
} else {
|
||||
console.warn('两个医生接口均未返回有效数据')
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('加载医生列表失败:', err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -446,7 +733,12 @@ function handleAdd() {
|
||||
form.value.patientGender = props.patientInfo.genderEnum_enumText
|
||||
form.value.patientAge = props.patientInfo.age
|
||||
form.value.applyDoctorName = userStore.nickName
|
||||
form.value.applyDeptName = props.patientInfo.deptName || ''
|
||||
form.value.applyDeptName = userStore.orgName || props.patientInfo.deptName || ''
|
||||
|
||||
// 确保医生列表已加载
|
||||
if (doctorList.value.length === 0) {
|
||||
loadDoctorList()
|
||||
}
|
||||
|
||||
// 加载诊断信息
|
||||
loadDiagnosisInfo()
|
||||
@@ -463,9 +755,39 @@ function handleEdit(row) {
|
||||
open.value = true
|
||||
isEditMode.value = true
|
||||
|
||||
// 确保医生列表已加载
|
||||
if (doctorList.value.length === 0) {
|
||||
loadDoctorList()
|
||||
}
|
||||
|
||||
getSurgeryDetail(row.id).then(res => {
|
||||
if (res.code === 200) {
|
||||
console.log('【编辑手术】完整返回数据:', res.data)
|
||||
console.log('【编辑手术】手术指征字段值:', res.data.surgeryIndication)
|
||||
|
||||
Object.assign(form.value, res.data)
|
||||
|
||||
console.log('【编辑手术】赋值后form.surgeryIndication:', form.value.surgeryIndication)
|
||||
|
||||
// 处理麻醉名称回显
|
||||
if (res.data.anesthesiaTypeEnum_dictText) {
|
||||
form.value.anesthesiaName = res.data.anesthesiaTypeEnum_dictText
|
||||
anesthesiaNameList.value = [{
|
||||
name: res.data.anesthesiaTypeEnum_dictText,
|
||||
id: 'default'
|
||||
}]
|
||||
}
|
||||
// 处理回显
|
||||
if (res.data.surgeryName) {
|
||||
surgeryNameList.value = [{
|
||||
name: res.data.surgeryName,
|
||||
busNo: res.data.surgeryCode
|
||||
}]
|
||||
}
|
||||
// 确保 secondarySurgeries 是数组
|
||||
if (!form.value.secondarySurgeries) {
|
||||
form.value.secondarySurgeries = []
|
||||
}
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取手术信息失败:', error)
|
||||
@@ -478,7 +800,18 @@ function handleView(row) {
|
||||
viewOpen.value = true
|
||||
getSurgeryDetail(row.id).then(res => {
|
||||
if (res.code === 200) {
|
||||
console.log('【手术详情】完整返回数据:', res.data)
|
||||
console.log('【手术详情】手术指征字段值:', res.data.surgeryIndication)
|
||||
console.log('【手术详情】手术指征字段类型:', typeof res.data.surgeryIndication)
|
||||
console.log('【手术详情】手术指征是否为空:', res.data.surgeryIndication === null || res.data.surgeryIndication === undefined || res.data.surgeryIndication === '')
|
||||
|
||||
viewData.value = res.data
|
||||
if (!viewData.value.secondarySurgeries) {
|
||||
viewData.value.secondarySurgeries = []
|
||||
}
|
||||
|
||||
console.log('【手术详情】赋值后viewData:', viewData.value)
|
||||
console.log('【手术详情】赋值后viewData.surgeryIndication:', viewData.value.surgeryIndication)
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取手术信息失败:', error)
|
||||
@@ -522,16 +855,158 @@ function handleRefresh() {
|
||||
proxy.$modal.msgSuccess('刷新成功')
|
||||
}
|
||||
|
||||
// 选择手术名称
|
||||
function selectSurgeryName() {
|
||||
proxy.$modal.msgInfo('请选择手术名称')
|
||||
// TODO: 实现手术名称选择弹窗
|
||||
// 远程搜索手术项目
|
||||
function remoteSearchSurgery(query) {
|
||||
surgeryLoading.value = true
|
||||
getDiagnosisTreatmentList({
|
||||
searchKey: query || '',
|
||||
pageNo: 1,
|
||||
pageSize: 100
|
||||
}).then(res => {
|
||||
// 处理不同的数据返回格式
|
||||
let data = []
|
||||
if (res.data && res.data.records) {
|
||||
data = res.data.records
|
||||
} else if (res.data && Array.isArray(res.data)) {
|
||||
data = res.data
|
||||
} else if (res.records && Array.isArray(res.records)) {
|
||||
data = res.records
|
||||
}
|
||||
|
||||
// 在前端进行手术分类过滤 (categoryCode 为 '24' 是手术)
|
||||
// 同时过滤掉停用的项 (statusEnum 为 2 是启用)
|
||||
surgeryNameList.value = data.filter(item =>
|
||||
(item.categoryCode === '24' || item.categoryCode_dictText === '手术') &&
|
||||
(item.statusEnum === 2 || item.statusEnum_enumText === '启用' || !item.statusEnum)
|
||||
)
|
||||
|
||||
surgeryLoading.value = false
|
||||
}).catch(error => {
|
||||
console.error('搜索手术项目失败:', error)
|
||||
surgeryLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 远程搜索麻醉项目
|
||||
function remoteSearchAnesthesia(query) {
|
||||
anesthesiaLoading.value = true
|
||||
getDiagnosisTreatmentList({
|
||||
searchKey: query || '',
|
||||
pageNo: 1,
|
||||
pageSize: 100
|
||||
}).then(res => {
|
||||
let data = []
|
||||
if (res.data && res.data.records) {
|
||||
data = res.data.records
|
||||
} else if (res.data && Array.isArray(res.data)) {
|
||||
data = res.data
|
||||
} else if (res.records && Array.isArray(res.records)) {
|
||||
data = res.records
|
||||
}
|
||||
|
||||
// 过滤麻醉项 (categoryCode 为 '25' 是麻醉)
|
||||
anesthesiaNameList.value = data.filter(item =>
|
||||
(item.categoryCode === '25' || item.categoryCode_dictText === '麻醉') &&
|
||||
(item.statusEnum === 2 || item.statusEnum_enumText === '启用' || !item.statusEnum)
|
||||
)
|
||||
|
||||
anesthesiaLoading.value = false
|
||||
}).catch(error => {
|
||||
console.error('搜索麻醉项目失败:', error)
|
||||
anesthesiaLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 手术项目选择变更
|
||||
function handleSurgeryChange(val) {
|
||||
const selected = surgeryNameList.value.find(item => item.name === val)
|
||||
if (selected) {
|
||||
form.value.surgeryCode = selected.busNo
|
||||
// 设置手术费用 (增加对多种字段名和类型的兼容)
|
||||
const price = selected.retailPrice ?? selected.retail_price ?? selected.price ?? selected.salePrice ?? 0
|
||||
form.value.surgeryFee = parseFloat(price)
|
||||
// 如果手术等级为空,可以尝试自动填充
|
||||
if (!form.value.surgeryLevel && selected.level) {
|
||||
form.value.surgeryLevel = selected.level
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 麻醉项目选择变更
|
||||
function handleAnesthesiaChange(val) {
|
||||
const selected = anesthesiaNameList.value.find(item => item.name === val)
|
||||
if (selected) {
|
||||
// 设置麻醉费用 (增加对多种字段名和类型的兼容)
|
||||
const price = selected.retailPrice ?? selected.retail_price ?? selected.price ?? selected.salePrice ?? 0
|
||||
form.value.anesthesiaFee = parseFloat(price)
|
||||
|
||||
// 尝试根据名称映射麻醉方式枚举
|
||||
form.value.anesthesiaTypeEnum = mapAnesthesiaNameToEnum(selected.name)
|
||||
}
|
||||
}
|
||||
|
||||
// 次要手术麻醉项目选择变更
|
||||
function handleSecondaryAnesthesiaChange(val, row) {
|
||||
const selected = anesthesiaNameList.value.find(item => item.name === val)
|
||||
if (selected) {
|
||||
// 设置该次要手术的麻醉费用
|
||||
const price = selected.retailPrice ?? selected.retail_price ?? selected.price ?? selected.salePrice ?? 0
|
||||
row.anesthesiaFee = parseFloat(price)
|
||||
row.anesthesiaTypeEnum = mapAnesthesiaNameToEnum(selected.name)
|
||||
row.anesthesiaName = selected.name
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助方法:将麻醉名称映射为枚举值
|
||||
function mapAnesthesiaNameToEnum(name) {
|
||||
if (!name) return undefined
|
||||
if (name.includes('局部')) return 1
|
||||
if (name.includes('区域')) return 2
|
||||
if (name.includes('全身') || name.includes('全麻')) return 3
|
||||
if (name.includes('脊椎')) return 4
|
||||
if (name.includes('硬膜外')) return 5
|
||||
if (name.includes('表面')) return 6
|
||||
return undefined
|
||||
}
|
||||
|
||||
// 次要手术项目选择变更
|
||||
function handleSecondarySurgeryChange(val, row) {
|
||||
const selected = surgeryNameList.value.find(item => item.name === val)
|
||||
if (selected) {
|
||||
row.surgeryCode = selected.busNo
|
||||
row.surgeryName = selected.name
|
||||
// 设置次要手术费用
|
||||
const price = selected.retailPrice ?? selected.retail_price ?? selected.price ?? selected.salePrice ?? 0
|
||||
row.surgeryFee = parseFloat(price)
|
||||
// 自动填充等级信息(如果后端返回了)
|
||||
if (selected.level) row.surgeryLevel = selected.level
|
||||
}
|
||||
}
|
||||
|
||||
// 添加次要手术
|
||||
function addAppendSurgery() {
|
||||
form.value.secondarySurgeries.push({
|
||||
surgeryName: '',
|
||||
surgeryCode: '',
|
||||
surgeryLevel: undefined,
|
||||
incisionLevel: undefined,
|
||||
anesthesiaTypeEnum: undefined
|
||||
})
|
||||
}
|
||||
|
||||
// 移除次要手术
|
||||
function removeSecondarySurgery(index) {
|
||||
form.value.secondarySurgeries.splice(index, 1)
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
function submitForm() {
|
||||
proxy.$refs['surgeryRef'].validate((valid) => {
|
||||
if (valid) {
|
||||
console.log('【提交表单】完整表单数据:', JSON.parse(JSON.stringify(form.value)))
|
||||
console.log('【提交表单】手术指征字段值:', form.value.surgeryIndication)
|
||||
console.log('【提交表单】手术指征字段类型:', typeof form.value.surgeryIndication)
|
||||
|
||||
if (form.value.id == undefined) {
|
||||
// 新增手术
|
||||
addSurgery(form.value).then((res) => {
|
||||
@@ -579,15 +1054,24 @@ function reset() {
|
||||
applyDeptName: undefined,
|
||||
surgeryNo: undefined,
|
||||
surgeryName: undefined,
|
||||
surgeryCode: undefined,
|
||||
surgeryTypeEnum: undefined,
|
||||
surgeryLevel: undefined,
|
||||
plannedTime: undefined,
|
||||
mainSurgeonId: undefined,
|
||||
assistantId: undefined,
|
||||
assistant1Id: undefined,
|
||||
assistant2Id: undefined,
|
||||
anesthesiaTypeEnum: undefined,
|
||||
anesthesiaName: undefined,
|
||||
anesthesiaFee: undefined,
|
||||
surgeryFee: undefined,
|
||||
totalFee: undefined,
|
||||
incisionLevel: undefined,
|
||||
preoperativeDiagnosis: undefined,
|
||||
surgeryIndication: undefined
|
||||
surgeryIndication: undefined,
|
||||
emergencyFlag: 0,
|
||||
implantFlag: 0,
|
||||
secondarySurgeries: []
|
||||
}
|
||||
if (surgeryRef.value) {
|
||||
surgeryRef.value.resetFields()
|
||||
|
||||
@@ -542,7 +542,17 @@ async function getListInfo(addNewRow) {
|
||||
tcmPrescriptionList.value.forEach((prescription) => {
|
||||
prescription.isAdding = false;
|
||||
});
|
||||
|
||||
// 如果没有encounterId,不调用接口
|
||||
if (!props.patientInfo || !props.patientInfo.encounterId) {
|
||||
console.warn('【中医处方】patientInfo或encounterId为空,不调用接口')
|
||||
tcmPrescriptionList.value = []
|
||||
return
|
||||
}
|
||||
|
||||
console.log('【中医处方】调用API,encounterId:', props.patientInfo.encounterId)
|
||||
await getTcmAdviceList({ encounterId: props.patientInfo.encounterId }).then(async (res) => {
|
||||
console.log('【中医处方】获取数据成功,处方数量:', res.data?.length || 0)
|
||||
// 按 groupId 分组数据
|
||||
const groupedData = {};
|
||||
res.data.forEach((item) => {
|
||||
@@ -623,7 +633,15 @@ async function getListInfo(addNewRow) {
|
||||
}
|
||||
|
||||
function getDiagnosisInfo() {
|
||||
// 如果没有encounterId,不调用接口
|
||||
if (!props.patientInfo || !props.patientInfo.encounterId) {
|
||||
console.warn('【中医诊断】patientInfo或encounterId为空,不调用接口')
|
||||
diagnosisList.value = []
|
||||
return
|
||||
}
|
||||
|
||||
diagnosisList.value = [];
|
||||
console.log('【中医诊断】调用API,encounterId:', props.patientInfo.encounterId)
|
||||
getTcmDiagnosis({ encounterId: props.patientInfo.encounterId }).then((res) => {
|
||||
if (res.data.illness.length > 0) {
|
||||
res.data.illness.forEach((item, index) => {
|
||||
@@ -635,6 +653,7 @@ function getDiagnosisInfo() {
|
||||
});
|
||||
});
|
||||
}
|
||||
console.log('【中医诊断】获取数据成功,诊断数量:', diagnosisList.value.length)
|
||||
// 默认选择第一个诊断
|
||||
if (diagnosisList.value.length > 0) {
|
||||
const firstDiagnosis = diagnosisList.value[0];
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
<el-table-column prop="requesterId_dictText" label="申请者" width="120" />
|
||||
<el-table-column label="操作" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" @click="handleViewDetail(scope.row)">详情</el-button>
|
||||
<el-button link type="primary" icon="View" @click="handleViewDetail(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -99,7 +99,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="detailDialogVisible = false">关闭</el-button>
|
||||
<el-button icon="Close" @click="detailDialogVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
@@ -105,6 +105,22 @@
|
||||
<!-- 手术等级:string - 三级手术 - 不可操作 -->
|
||||
<el-table-column label="手术等级" align="center" prop="surgeryLevel_dictText" width="90" />
|
||||
|
||||
<!-- 急诊标志:bool - 是/否 - 不可操作 -->
|
||||
<el-table-column label="急诊" align="center" prop="emergencyFlag" width="70">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.emergencyFlag === 1" type="danger" size="small">是</el-tag>
|
||||
<el-tag v-else type="info" size="small">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 植入高值耗材标志:bool - 是/否 - 不可操作 -->
|
||||
<el-table-column label="植入耗材" align="center" prop="implantFlag" width="80">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.implantFlag === 1" type="warning" size="small">是</el-tag>
|
||||
<el-tag v-else type="info" size="small">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 计划时间:datetime - 2025-09-20 08:00:00 - 不可操作 -->
|
||||
<el-table-column label="计划时间" align="center" prop="plannedTime" width="160">
|
||||
<template #default="scope">
|
||||
@@ -490,6 +506,41 @@
|
||||
<el-input v-model="form.complications" type="textarea" placeholder="请输入并发症描述" :rows="3" :disabled="isViewMode" />
|
||||
</el-form-item>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="急诊标志" prop="emergencyFlag">
|
||||
<el-radio-group v-model="form.emergencyFlag" :disabled="isViewMode">
|
||||
<el-radio :value="0">否</el-radio>
|
||||
<el-radio :value="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="植入高值耗材标志" prop="implantFlag">
|
||||
<el-radio-group v-model="form.implantFlag" :disabled="isViewMode">
|
||||
<el-radio :value="0">否</el-radio>
|
||||
<el-radio :value="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="手术室确认人" prop="operatingRoomConfirmUser">
|
||||
<el-input v-model="form.operatingRoomConfirmUser" placeholder="手术室确认人" :disabled="isViewMode" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="手术室确认时间" prop="operatingRoomConfirmTime">
|
||||
<el-date-picker
|
||||
v-model="form.operatingRoomConfirmTime"
|
||||
type="datetime"
|
||||
placeholder="选择手术室确认时间"
|
||||
value-format="YYYY-MM-DDTHH:mm:ss"
|
||||
style="width: 100%"
|
||||
:disabled="isViewMode"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注信息" :rows="2" :disabled="isViewMode" />
|
||||
</el-form-item>
|
||||
@@ -564,7 +615,11 @@ const form = ref({
|
||||
incisionLevel: undefined,
|
||||
healingLevel: undefined,
|
||||
complications: undefined,
|
||||
remark: undefined
|
||||
remark: undefined,
|
||||
emergencyFlag: 0,
|
||||
implantFlag: 0,
|
||||
operatingRoomConfirmTime: undefined,
|
||||
operatingRoomConfirmUser: undefined
|
||||
})
|
||||
const surgeryRef = ref()
|
||||
const total = ref(0)
|
||||
@@ -957,6 +1012,14 @@ function handleEdit(row) {
|
||||
form.value.complications = data.complications
|
||||
form.value.remark = data.remark
|
||||
form.value.statusEnum = data.statusEnum
|
||||
form.value.emergencyFlag = data.emergencyFlag || 0
|
||||
form.value.implantFlag = data.implantFlag || 0
|
||||
form.value.operatingRoomConfirmTime = data.operatingRoomConfirmTime
|
||||
form.value.operatingRoomConfirmUser = data.operatingRoomConfirmUser
|
||||
form.value.emergencyFlag = data.emergencyFlag || 0
|
||||
form.value.implantFlag = data.implantFlag || 0
|
||||
form.value.operatingRoomConfirmTime = data.operatingRoomConfirmTime
|
||||
form.value.operatingRoomConfirmUser = data.operatingRoomConfirmUser
|
||||
|
||||
// 打印赋值后的表单数据
|
||||
console.log('赋值后的表单数据:', {
|
||||
@@ -1054,6 +1117,14 @@ function handleView(row) {
|
||||
form.value.complications = data.complications
|
||||
form.value.remark = data.remark
|
||||
form.value.statusEnum = data.statusEnum
|
||||
form.value.emergencyFlag = data.emergencyFlag || 0
|
||||
form.value.implantFlag = data.implantFlag || 0
|
||||
form.value.operatingRoomConfirmTime = data.operatingRoomConfirmTime
|
||||
form.value.operatingRoomConfirmUser = data.operatingRoomConfirmUser
|
||||
form.value.emergencyFlag = data.emergencyFlag || 0
|
||||
form.value.implantFlag = data.implantFlag || 0
|
||||
form.value.operatingRoomConfirmTime = data.operatingRoomConfirmTime
|
||||
form.value.operatingRoomConfirmUser = data.operatingRoomConfirmUser
|
||||
|
||||
// 打印赋值后的表单数据
|
||||
console.log('【查看模式】赋值后的表单数据:', {
|
||||
@@ -1139,7 +1210,11 @@ function reset() {
|
||||
form.value.healingLevel = undefined
|
||||
form.value.complications = undefined
|
||||
form.value.remark = undefined
|
||||
|
||||
form.value.emergencyFlag = 0
|
||||
form.value.implantFlag = 0
|
||||
form.value.operatingRoomConfirmTime = undefined
|
||||
form.value.operatingRoomConfirmUser = undefined
|
||||
|
||||
if (surgeryRef.value) {
|
||||
surgeryRef.value.resetFields()
|
||||
}
|
||||
|
||||
120
手术和麻醉信息Redis缓存实现说明.md
Normal file
120
手术和麻醉信息Redis缓存实现说明.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# 手术和麻醉信息Redis缓存实现说明
|
||||
|
||||
## 概述
|
||||
为提高手术和麻醉信息的查询性能,已将手术信息缓存到Redis中。接口查询时先从Redis缓存获取,如果没有则从数据库查询并更新到Redis缓存。
|
||||
|
||||
## 实现细节
|
||||
|
||||
### 1. Redis缓存Key定义
|
||||
在 `openhis-common/src/main/java/com/openhis/common/utils/RedisKeys.java` 中定义了以下缓存Key:
|
||||
|
||||
```java
|
||||
// 单个手术信息缓存
|
||||
public static String getSurgeryKey(Long surgeryId)
|
||||
|
||||
// 按患者ID查询的手术列表缓存
|
||||
public static String getSurgeryListByPatientKey(Long patientId)
|
||||
|
||||
// 按就诊ID查询的手术列表缓存
|
||||
public static String getSurgeryListByEncounterKey(Long encounterId)
|
||||
```
|
||||
|
||||
### 2. 缓存实现
|
||||
|
||||
#### 2.1 SurgeryServiceImpl (Domain层)
|
||||
- **getSurgeryById(Long id)**: 根据手术ID查询单个手术信息
|
||||
- 先从Redis缓存获取
|
||||
- 缓存未命中则从数据库查询
|
||||
- 查询结果存入Redis缓存(30分钟过期)
|
||||
|
||||
- **getSurgeryListByPatientId(Long patientId)**: 根据患者ID查询手术列表
|
||||
- 先从Redis缓存获取
|
||||
- 缓存未命中则从数据库查询
|
||||
- 查询结果存入Redis缓存(30分钟过期)
|
||||
|
||||
- **getSurgeryListByEncounterId(Long encounterId)**: 根据就诊ID查询手术列表
|
||||
- 先从Redis缓存获取
|
||||
- 缓存未命中则从数据库查询
|
||||
- 查询结果存入Redis缓存(30分钟过期)
|
||||
|
||||
- **insertSurgery(Surgery surgery)**: 新增手术信息
|
||||
- 插入成功后清除相关缓存
|
||||
|
||||
- **updateSurgery(Surgery surgery)**: 更新手术信息
|
||||
- 更新成功后清除相关缓存
|
||||
|
||||
- **deleteSurgery(Long id)**: 删除手术信息
|
||||
- 删除成功后清除相关缓存
|
||||
|
||||
- **updateSurgeryStatus(Long id, Integer statusEnum)**: 更新手术状态
|
||||
- 更新成功后清除相关缓存
|
||||
|
||||
#### 2.2 SurgeryAppServiceImpl (Application层)
|
||||
- **getSurgeryDetail(Long id)**: 根据ID查询手术详情
|
||||
- 先从Redis缓存获取
|
||||
- 缓存未命中则从数据库查询
|
||||
- 查询结果存入Redis缓存(30分钟过期)
|
||||
|
||||
- **addSurgery(SurgeryDto surgeryDto)**: 新增手术信息
|
||||
- 插入成功后清除相关缓存
|
||||
|
||||
- **updateSurgery(SurgeryDto surgeryDto)**: 更新手术信息
|
||||
- 更新成功后清除相关缓存
|
||||
|
||||
- **deleteSurgery(Long id)**: 删除手术信息
|
||||
- 删除成功后清除相关缓存
|
||||
|
||||
- **updateSurgeryStatus(Long id, Integer statusEnum)**: 更新手术状态
|
||||
- 更新成功后清除相关缓存
|
||||
|
||||
### 3. 缓存清除策略
|
||||
当手术信息发生变化时(新增、更新、删除),会清除以下相关缓存:
|
||||
1. 单个手术信息缓存
|
||||
2. 患者手术列表缓存
|
||||
3. 就诊手术列表缓存
|
||||
|
||||
### 4. 缓存配置
|
||||
- **缓存时间**: 30分钟
|
||||
- **时间单位**: TimeUnit.MINUTES
|
||||
- **序列化**: 使用RedisTemplate默认序列化方式
|
||||
|
||||
## 关于麻醉信息
|
||||
当前项目中,麻醉信息是手术表(`cli_surgery`)中的字段,包括:
|
||||
- 麻醉医生ID (anesthetistId)
|
||||
- 麻醉医生姓名 (anesthetistName)
|
||||
- 麻醉方式编码 (anesthesiaTypeEnum)
|
||||
- 麻醉费用 (anesthesiaFee)
|
||||
|
||||
这些字段已经包含在手术实体的缓存中,无需单独实现麻醉信息的缓存。
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 查询手术信息(自动使用缓存)
|
||||
```java
|
||||
// 自动从缓存获取,未命中则查询数据库
|
||||
Surgery surgery = surgeryService.getSurgeryById(surgeryId);
|
||||
```
|
||||
|
||||
### 更新手术信息(自动清除缓存)
|
||||
```java
|
||||
// 更新数据库,同时清除相关缓存
|
||||
surgeryService.updateSurgery(surgery);
|
||||
```
|
||||
|
||||
### 手动清除缓存(如需要)
|
||||
```java
|
||||
String cacheKey = RedisKeys.getSurgeryKey(surgeryId);
|
||||
redisCache.deleteObject(cacheKey);
|
||||
```
|
||||
|
||||
## 性能优化建议
|
||||
1. 对于频繁访问的手术信息,缓存命中率高,可显著提升查询性能
|
||||
2. 对于不常访问的手术信息,30分钟缓存时间可避免占用过多Redis内存
|
||||
3. 如需调整缓存时间,可修改代码中的 `30, TimeUnit.MINUTES` 参数
|
||||
4. 如需更精细的缓存控制,可考虑使用不同的缓存时间策略(如根据手术状态设置不同过期时间)
|
||||
|
||||
## 监控建议
|
||||
建议监控以下指标:
|
||||
- Redis缓存命中率
|
||||
- Redis内存使用情况
|
||||
- 查询响应时间对比(缓存命中 vs 缓存未命中)
|
||||
25
检查surgery_indication字段.sql
Normal file
25
检查surgery_indication字段.sql
Normal file
@@ -0,0 +1,25 @@
|
||||
-- 检查cli_surgery表中是否有surgery_indication字段
|
||||
SELECT
|
||||
column_name,
|
||||
data_type,
|
||||
is_nullable,
|
||||
column_default
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'cli_surgery'
|
||||
AND column_name = 'surgery_indication';
|
||||
|
||||
-- 查看cli_surgery表的所有字段
|
||||
SELECT
|
||||
column_name,
|
||||
data_type,
|
||||
is_nullable,
|
||||
column_default
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'cli_surgery'
|
||||
ORDER BY ordinal_position;
|
||||
|
||||
-- 如果字段不存在,添加该字段
|
||||
-- ALTER TABLE cli_surgery ADD COLUMN surgery_indication TEXT;
|
||||
-- COMMENT ON COLUMN cli_surgery.surgery_indication IS '手术指征';
|
||||
114
迁移记录-DB变更记录/20260105_fill_surgery_name_fields.sql
Normal file
114
迁移记录-DB变更记录/20260105_fill_surgery_name_fields.sql
Normal file
@@ -0,0 +1,114 @@
|
||||
-- 填充手术表中的name字段
|
||||
-- 执行时间:2025-01-05
|
||||
-- 说明:根据现有的ID字段反查并填充对应的name字段
|
||||
|
||||
-- 1. 查看当前name字段的填充情况
|
||||
SELECT
|
||||
COUNT(*) as total_count,
|
||||
COUNT(main_surgeon_name) as has_main_surgeon_name_count,
|
||||
COUNT(anesthetist_name) as has_anesthetist_name_count,
|
||||
COUNT(assistant_1_name) as has_assistant_1_name_count,
|
||||
COUNT(assistant_2_name) as has_assistant_2_name_count,
|
||||
COUNT(scrub_nurse_name) as has_scrub_nurse_name_count,
|
||||
COUNT(operating_room_name) as has_operating_room_name_count,
|
||||
COUNT(org_name) as has_org_name_count,
|
||||
COUNT(apply_doctor_name) as has_apply_doctor_name_count,
|
||||
COUNT(apply_dept_name) as has_apply_dept_name_count
|
||||
FROM public.cli_surgery
|
||||
WHERE delete_flag = '0';
|
||||
|
||||
-- 2. 填充主刀医生姓名
|
||||
UPDATE public.cli_surgery s
|
||||
SET main_surgeon_name = u.nick_name
|
||||
FROM public.sys_user u
|
||||
WHERE s.main_surgeon_id = u.user_id
|
||||
AND s.main_surgeon_name IS NULL
|
||||
AND s.delete_flag = '0';
|
||||
|
||||
-- 3. 填充麻醉医生姓名
|
||||
UPDATE public.cli_surgery s
|
||||
SET anesthetist_name = u.nick_name
|
||||
FROM public.sys_user u
|
||||
WHERE s.anesthetist_id = u.user_id
|
||||
AND s.anesthetist_name IS NULL
|
||||
AND s.delete_flag = '0';
|
||||
|
||||
-- 4. 填充助手1姓名
|
||||
UPDATE public.cli_surgery s
|
||||
SET assistant_1_name = u.nick_name
|
||||
FROM public.sys_user u
|
||||
WHERE s.assistant_1_id = u.user_id
|
||||
AND s.assistant_1_name IS NULL
|
||||
AND s.delete_flag = '0';
|
||||
|
||||
-- 5. 填充助手2姓名
|
||||
UPDATE public.cli_surgery s
|
||||
SET assistant_2_name = u.nick_name
|
||||
FROM public.sys_user u
|
||||
WHERE s.assistant_2_id = u.user_id
|
||||
AND s.assistant_2_name IS NULL
|
||||
AND s.delete_flag = '0';
|
||||
|
||||
-- 6. 填充巡回护士姓名
|
||||
UPDATE public.cli_surgery s
|
||||
SET scrub_nurse_name = u.nick_name
|
||||
FROM public.sys_user u
|
||||
WHERE s.scrub_nurse_id = u.user_id
|
||||
AND s.scrub_nurse_name IS NULL
|
||||
AND s.delete_flag = '0';
|
||||
|
||||
-- 7. 填充手术室名称
|
||||
UPDATE public.cli_surgery s
|
||||
SET operating_room_name = r.name
|
||||
FROM public.cli_operating_room r
|
||||
WHERE s.operating_room_id = r.id
|
||||
AND s.operating_room_name IS NULL
|
||||
AND s.delete_flag = '0';
|
||||
|
||||
-- 8. 填充执行科室名称
|
||||
UPDATE public.cli_surgery s
|
||||
SET org_name = o.name
|
||||
FROM public.adm_organization o
|
||||
WHERE s.org_id = o.id
|
||||
AND s.org_name IS NULL
|
||||
AND s.delete_flag = '0';
|
||||
|
||||
-- 9. 填充申请医生姓名(如果还没有)
|
||||
UPDATE public.cli_surgery s
|
||||
SET apply_doctor_name = u.nick_name
|
||||
FROM public.sys_user u
|
||||
WHERE s.apply_doctor_id = u.user_id
|
||||
AND s.apply_doctor_name IS NULL
|
||||
AND s.delete_flag = '0';
|
||||
|
||||
-- 10. 填充申请科室名称(如果还没有)
|
||||
UPDATE public.cli_surgery s
|
||||
SET apply_dept_name = o.name
|
||||
FROM public.adm_organization o
|
||||
WHERE s.apply_dept_id = o.id
|
||||
AND s.apply_dept_name IS NULL
|
||||
AND s.delete_flag = '0';
|
||||
|
||||
-- 11. 再次查询,验证更新结果
|
||||
SELECT
|
||||
id,
|
||||
surgery_no,
|
||||
patient_name,
|
||||
apply_doctor_name,
|
||||
apply_dept_name,
|
||||
main_surgeon_name,
|
||||
anesthetist_name,
|
||||
assistant_1_name,
|
||||
assistant_2_name,
|
||||
scrub_nurse_name,
|
||||
operating_room_name,
|
||||
org_name,
|
||||
emergency_flag,
|
||||
implant_flag,
|
||||
operating_room_confirm_time,
|
||||
operating_room_confirm_user,
|
||||
create_time
|
||||
FROM public.cli_surgery
|
||||
WHERE delete_flag = '0'
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 10;
|
||||
53
迁移记录-DB变更记录/20260106_check_and_add_surgery_indication.sql
Normal file
53
迁移记录-DB变更记录/20260106_check_and_add_surgery_indication.sql
Normal file
@@ -0,0 +1,53 @@
|
||||
-- 检查并添加手术指征字段
|
||||
-- 执行时间:2025-01-06
|
||||
-- 说明:修复手术指征字段无法保存或展示的问题
|
||||
|
||||
-- 1. 检查surgery_indication字段是否存在
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'cli_surgery'
|
||||
AND column_name = 'surgery_indication'
|
||||
) THEN
|
||||
-- 字段不存在,添加该字段
|
||||
ALTER TABLE cli_surgery ADD COLUMN surgery_indication TEXT;
|
||||
|
||||
-- 添加字段注释
|
||||
COMMENT ON COLUMN cli_surgery.surgery_indication IS '手术指征';
|
||||
|
||||
RAISE NOTICE '已添加 surgery_indication 字段到 cli_surgery 表';
|
||||
ELSE
|
||||
RAISE NOTICE 'surgery_indication 字段已存在于 cli_surgery 表';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 2. 验证字段是否添加成功
|
||||
SELECT
|
||||
column_name,
|
||||
data_type,
|
||||
is_nullable,
|
||||
column_default,
|
||||
character_maximum_length
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'cli_surgery'
|
||||
AND column_name = 'surgery_indication';
|
||||
|
||||
-- 3. 查看最近的手术记录,检查surgery_indication字段是否有数据
|
||||
SELECT
|
||||
id,
|
||||
surgery_no,
|
||||
surgery_name,
|
||||
surgery_indication,
|
||||
create_time
|
||||
FROM cli_surgery
|
||||
WHERE delete_flag = '0'
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 5;
|
||||
|
||||
-- 4. 更新说明
|
||||
-- 手术指征字段已添加,可以正常保存和展示数据
|
||||
-- 该字段为TEXT类型,可以存储较长的文本内容
|
||||
59
验证手术指征字段返回数据.sql
Normal file
59
验证手术指征字段返回数据.sql
Normal file
@@ -0,0 +1,59 @@
|
||||
-- 验证手术指征字段的数据
|
||||
-- 执行时间:2025-01-06
|
||||
-- 说明:查询手术数据,验证surgery_indication字段是否有值
|
||||
|
||||
-- 1. 查询最近的手术记录,检查surgery_indication字段
|
||||
SELECT
|
||||
id,
|
||||
surgery_no,
|
||||
surgery_name,
|
||||
surgery_indication,
|
||||
patient_name,
|
||||
apply_doctor_name,
|
||||
status_enum,
|
||||
create_time
|
||||
FROM cli_surgery
|
||||
WHERE delete_flag = '0'
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 5;
|
||||
|
||||
-- 2. 检查surgery_indication字段是否为NULL
|
||||
SELECT
|
||||
COUNT(*) as total_count,
|
||||
COUNT(surgery_indication) as has_surgery_indication_count,
|
||||
COUNT(*) - COUNT(surgery_indication) as null_count,
|
||||
ROUND(COUNT(surgery_indication) * 100.0 / COUNT(*), 2) as fill_rate
|
||||
FROM cli_surgery
|
||||
WHERE delete_flag = '0';
|
||||
|
||||
-- 3. 查询有surgery_indication数据的记录
|
||||
SELECT
|
||||
id,
|
||||
surgery_no,
|
||||
surgery_name,
|
||||
surgery_indication,
|
||||
LENGTH(surgery_indication) as text_length,
|
||||
SUBSTRING(surgery_indication, 1, 100) as preview_text
|
||||
FROM cli_surgery
|
||||
WHERE delete_flag = '0'
|
||||
AND surgery_indication IS NOT NULL
|
||||
AND surgery_indication != ''
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 3;
|
||||
|
||||
-- 4. 验证Mapper XML中的SQL查询
|
||||
-- 模拟getSurgeryDetail接口的查询
|
||||
SELECT
|
||||
s.id,
|
||||
s.surgery_no,
|
||||
s.surgery_name,
|
||||
s.surgery_indication,
|
||||
s.patient_id,
|
||||
p.name as patient_name,
|
||||
s.apply_doctor_id,
|
||||
s.apply_doctor_name,
|
||||
s.status_enum
|
||||
FROM cli_surgery s
|
||||
LEFT JOIN adm_patient p ON s.patient_id = p.id
|
||||
WHERE s.id = (SELECT id FROM cli_surgery WHERE delete_flag = '0' ORDER BY create_time DESC LIMIT 1)
|
||||
AND s.delete_flag = '0';
|
||||
Reference in New Issue
Block a user