Merge remote-tracking branch 'origin/develop' into develop

# Conflicts:
#	healthlink-his-ui/src/views/inpatientDoctor/home/components/diagnosis/chineseMedicineDialog.vue
#	healthlink-his-ui/src/views/inpatientDoctor/home/components/diagnosis/diagnosis.vue
This commit is contained in:
2026-06-28 07:04:03 +08:00
31 changed files with 720 additions and 491 deletions

5
.claude/settings.json Normal file
View File

@@ -0,0 +1,5 @@
{
"enabledPlugins": {
"agent-sdk-dev@claude-plugins-official": true
}
}

View File

@@ -35,9 +35,10 @@ public interface IOutpatientChargeAppService {
* 根据就诊id查询患者处方列表 * 根据就诊id查询患者处方列表
* *
* @param encounterId 就诊id * @param encounterId 就诊id
* @param statusEnum 收费状态过滤(可选,不传则返回全部状态)
* @return 患者处方列表 * @return 患者处方列表
*/ */
List<EncounterPatientPrescriptionDto> getEncounterPatientPrescription(Long encounterId); List<EncounterPatientPrescriptionDto> getEncounterPatientPrescription(Long encounterId, Integer statusEnum);
/** /**
* 根据就诊id查询患者处方列表并新增字段实收金额、应收金额、优惠金额、折扣率 * 根据就诊id查询患者处方列表并新增字段实收金额、应收金额、优惠金额、折扣率

View File

@@ -111,10 +111,11 @@ public class OutpatientChargeAppServiceImpl implements IOutpatientChargeAppServi
* 根据就诊id查询患者处方列表 * 根据就诊id查询患者处方列表
* *
* @param encounterId 就诊id * @param encounterId 就诊id
* @param statusEnum 收费状态过滤(可选,不传则返回全部状态)
* @return 患者处方列表 * @return 患者处方列表
*/ */
@Override @Override
public List<EncounterPatientPrescriptionDto> getEncounterPatientPrescription(Long encounterId) { public List<EncounterPatientPrescriptionDto> getEncounterPatientPrescription(Long encounterId, Integer statusEnum) {
List<EncounterPatientPrescriptionDto> prescriptionDtoList List<EncounterPatientPrescriptionDto> prescriptionDtoList
= outpatientChargeAppMapper.selectEncounterPatientPrescription(encounterId, = outpatientChargeAppMapper.selectEncounterPatientPrescription(encounterId,
ChargeItemContext.ACTIVITY.getValue(), ChargeItemContext.MEDICATION.getValue(), ChargeItemContext.ACTIVITY.getValue(), ChargeItemContext.MEDICATION.getValue(),
@@ -123,7 +124,7 @@ public class OutpatientChargeAppServiceImpl implements IOutpatientChargeAppServi
ChargeItemStatus.PLANNED.getValue(), ChargeItemStatus.BILLABLE.getValue(), ChargeItemStatus.PLANNED.getValue(), ChargeItemStatus.BILLABLE.getValue(),
ChargeItemStatus.BILLED.getValue(), ChargeItemStatus.REFUNDING.getValue(), ChargeItemStatus.BILLED.getValue(), ChargeItemStatus.REFUNDING.getValue(),
ChargeItemStatus.REFUNDED.getValue(), ChargeItemStatus.PART_REFUND.getValue(), ChargeItemStatus.REFUNDED.getValue(), ChargeItemStatus.PART_REFUND.getValue(),
CommonConstants.TableName.WOR_DEVICE_REQUEST); CommonConstants.TableName.WOR_DEVICE_REQUEST, statusEnum);
prescriptionDtoList.forEach(e -> { prescriptionDtoList.forEach(e -> {
// 收费状态枚举 // 收费状态枚举
e.setStatusEnum_enumText(EnumUtils.getInfoByValue(ChargeItemStatus.class, e.getStatusEnum())); e.setStatusEnum_enumText(EnumUtils.getInfoByValue(ChargeItemStatus.class, e.getStatusEnum()));

View File

@@ -61,11 +61,13 @@ public class OutpatientChargeController {
* 根据就诊id查询患者处方列表 * 根据就诊id查询患者处方列表
* *
* @param encounterId 就诊id * @param encounterId 就诊id
* @param statusEnum 收费状态过滤(可选,不传则返回全部状态)
* @return 患者处方列表 * @return 患者处方列表
*/ */
@GetMapping(value = "/patient-prescription") @GetMapping(value = "/patient-prescription")
public R<?> getEncounterPatientPrescription(@RequestParam Long encounterId) { public R<?> getEncounterPatientPrescription(@RequestParam Long encounterId,
return R.ok(outpatientChargeAppService.getEncounterPatientPrescription(encounterId)); @RequestParam(required = false) Integer statusEnum) {
return R.ok(outpatientChargeAppService.getEncounterPatientPrescription(encounterId, statusEnum));
} }
/** /**

View File

@@ -59,7 +59,8 @@ public interface OutpatientChargeAppMapper {
@Param("chinesePatentMedicine") Integer chinesePatentMedicine, @Param("chinesePatentMedicine") Integer chinesePatentMedicine,
@Param("planned") Integer planned, @Param("billable") Integer billable, @Param("planned") Integer planned, @Param("billable") Integer billable,
@Param("billed") Integer billed, @Param("refunding") Integer refunding, @Param("refunded") Integer refunded, @Param("billed") Integer billed, @Param("refunding") Integer refunding, @Param("refunded") Integer refunded,
@Param("partRefund") Integer partRefund, @Param("worDeviceRequest") String worDeviceRequest); @Param("partRefund") Integer partRefund, @Param("worDeviceRequest") String worDeviceRequest,
@Param("filterStatus") Integer filterStatus);
/** /**
* 根据就诊id查询患者处方列表并新增字段应收金额实收金额优惠金额折扣率 * 根据就诊id查询患者处方列表并新增字段应收金额实收金额优惠金额折扣率

View File

@@ -521,9 +521,9 @@ public class CommonServiceImpl implements ICommonService {
} }
} }
// 方式 3如果仍然没有病区且 currentOrgId 为空,尝试获取所有病区 // 方式 3如果仍然没有病区尝试获取所有活动病区
if (wardList.isEmpty() && currentOrgId == null) { if (wardList.isEmpty()) {
log.info("getPractitionerWard - 尝试获取所有病区"); log.info("getPractitionerWard - 未查到指定病区,获取所有病区");
List<Location> allWards = locationService.getWardList(null); List<Location> allWards = locationService.getWardList(null);
log.info("getPractitionerWard - 所有病区数:{}", allWards != null ? allWards.size() : 0); log.info("getPractitionerWard - 所有病区数:{}", allWards != null ? allWards.size() : 0);
if (allWards != null && !allWards.isEmpty()) { if (allWards != null && !allWards.isEmpty()) {

View File

@@ -140,8 +140,8 @@ public class DoctorStationChineseMedicalAppServiceImpl implements IDoctorStation
.setSourceEnum(ConditionDefinitionSource.TRADITIONAL_CHINESE_MEDICINE_SYNDROME_CATALOG.getValue()); .setSourceEnum(ConditionDefinitionSource.TRADITIONAL_CHINESE_MEDICINE_SYNDROME_CATALOG.getValue());
QueryWrapper<ConditionDefinition> queryWrapper = HisQueryUtils.buildQueryWrapper(conditionDefinition, searchKey, QueryWrapper<ConditionDefinition> queryWrapper = HisQueryUtils.buildQueryWrapper(conditionDefinition, searchKey,
new HashSet<>(Arrays.asList("name", "py_str", "wb_str")), null); new HashSet<>(Arrays.asList("name", "py_str", "wb_str")), null);
// 设置排序 // 设置排序(与诊断目录页保持一致,按编码升序,确保取到原始标准编码记录)
queryWrapper.orderByDesc("update_time"); queryWrapper.orderByAsc("condition_code");
// 诊断信息 // 诊断信息
Page<ConditionDefinitionMetadata> conditionDefinitionMetadataPage = HisPageUtils Page<ConditionDefinitionMetadata> conditionDefinitionMetadataPage = HisPageUtils
.selectPage(conditionDefinitionMapper, queryWrapper, pageNo, pageSize, ConditionDefinitionMetadata.class); .selectPage(conditionDefinitionMapper, queryWrapper, pageNo, pageSize, ConditionDefinitionMetadata.class);

View File

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.core.common.core.domain.R; import com.core.common.core.domain.R;
import com.core.common.enums.DelFlag;
import com.core.common.exception.ServiceException; import com.core.common.exception.ServiceException;
import com.core.common.utils.*; import com.core.common.utils.*;
import com.core.common.utils.bean.BeanUtils; import com.core.common.utils.bean.BeanUtils;
@@ -87,6 +88,12 @@ public class InHospitalRegisterAppServiceImpl implements IInHospitalRegisterAppS
@Resource @Resource
private IChargeItemService iChargeItemService; private IChargeItemService iChargeItemService;
@Resource
private ILocationService iLocationService;
@Resource
private IOrganizationService iOrganizationService;
/** /**
* 门诊医生开住院申请 * 门诊医生开住院申请
* *
@@ -357,17 +364,38 @@ public class InHospitalRegisterAppServiceImpl implements IInHospitalRegisterAppS
*/ */
@Override @Override
public List<LocationDto> getWardList(Long orgId) { public List<LocationDto> getWardList(Long orgId) {
List<Location> wardList = inHospitalRegisterAppMapper.selectWardList(orgId, LocationForm.WARD.getValue(), Set<Long> orgIds = new LinkedHashSet<>();
LocationStatus.ACTIVE.getValue()); if (orgId != null) {
orgIds.add(orgId);
// 2. 转换为 LocationDto逻辑与原代码完全一致 // 通过 busNo 层级查找所有子孙科室busNo 用 "." 分隔层级,如 "1" → "1.1" → "1.1.1"
List<LocationDto> locationDtoList = new ArrayList<>(); Organization org = iOrganizationService.getById(orgId);
for (Location location : wardList) { if (org != null && StringUtils.isNotEmpty(org.getBusNo())) {
LocationDto locationDto = new LocationDto(); List<Organization> children = iOrganizationService.list(
BeanUtils.copyProperties(location, locationDto); new LambdaQueryWrapper<Organization>()
locationDtoList.add(locationDto); .likeRight(Organization::getBusNo, org.getBusNo() + ".")
.eq(Organization::getDeleteFlag, DelFlag.NO.getCode()));
for (Organization child : children) {
orgIds.add(child.getId());
}
}
} }
// 查询所有关联科室下的病区
List<Location> wardList = new ArrayList<>();
for (Long id : orgIds) {
wardList.addAll(iLocationService.getWardList(id));
}
// 按 ID 去重
List<LocationDto> locationDtoList = new ArrayList<>();
Set<Long> seen = new HashSet<>();
for (Location location : wardList) {
if (seen.add(location.getId())) {
LocationDto dto = new LocationDto();
BeanUtils.copyProperties(location, dto);
locationDtoList.add(dto);
}
}
return locationDtoList; return locationDtoList;
} }

View File

@@ -268,15 +268,9 @@ public class AdviceProcessAppServiceImpl implements IAdviceProcessAppService {
e.setSingleDose(doseStr + unitStr); e.setSingleDose(doseStr + unitStr);
} }
// 总量:剂量 × 数量 + 单位(仅药品医嘱 // 总量:数量(总量就是数量,不需要乘以剂量,单位由前端显示
if (e.getDose() != null && e.getQuantity() != null) { if (e.getQuantity() != null) {
BigDecimal total = e.getDose().multiply(BigDecimal.valueOf(e.getQuantity())); e.setTotalAmount(String.valueOf(e.getQuantity()));
String totalStr = total.stripTrailingZeros().toPlainString();
String unitStr = e.getUnitCode_dictText() != null ? e.getUnitCode_dictText() : "";
e.setTotalAmount(totalStr + unitStr);
} else if (e.getQuantity() != null) {
String unitStr = e.getUnitCode_dictText() != null ? e.getUnitCode_dictText() : "";
e.setTotalAmount(e.getQuantity() + unitStr);
} }
// 频次/用法组合 // 频次/用法组合

View File

@@ -72,6 +72,10 @@ public interface IChargeBillService {
*/ */
R<?> checkYbNo(); R<?> checkYbNo();
/**
* 分页查询收费账单列表
*/
Map<String, Object> getBillPage(String searchKey, String billType, String payStatus,
String startTime, String endTime, Integer pageNo, Integer pageSize);
} }

View File

@@ -608,4 +608,221 @@ public class ChargeBillQueryService {
map.put("insutype", "自费"); map.put("insutype", "自费");
return map; return map;
} }
/**
* 分页查询收费账单列表
*/
public Map<String, Object> getBillPage(String searchKey, String billType, String payStatus,
String startTime, String endTime, Integer pageNo, Integer pageSize) {
// 预解析searchKey避免后续重复查询
List<Long> searchPatientIds = null;
List<Long> searchEncounterIds = null;
if (StringUtils.isNotEmpty(searchKey)) {
searchPatientIds = iPatientService.list(new LambdaQueryWrapper<Patient>()
.like(Patient::getName, searchKey)
.eq(Patient::getDeleteFlag, DelFlag.NO.getCode())
.select(Patient::getId))
.stream().map(Patient::getId).collect(Collectors.toList());
searchEncounterIds = iEncounterService.list(new LambdaQueryWrapper<Encounter>()
.like(Encounter::getBusNo, searchKey)
.eq(Encounter::getDeleteFlag, DelFlag.NO.getCode())
.select(Encounter::getId))
.stream().map(Encounter::getId).collect(Collectors.toList());
}
// 分页查询
LambdaQueryWrapper<PaymentReconciliation> pageWrapper = buildBillQueryWrapper(
billType, payStatus, startTime, endTime, searchPatientIds, searchEncounterIds);
pageWrapper.orderByDesc(PaymentReconciliation::getBillDate);
com.baomidou.mybatisplus.extension.plugins.pagination.Page<PaymentReconciliation> page =
new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(pageNo, pageSize);
List<PaymentReconciliation> billList = paymentReconciliationService.page(page, pageWrapper).getRecords();
// 总数
long total = paymentReconciliationService.count(buildBillQueryWrapper(
billType, payStatus, startTime, endTime, searchPatientIds, searchEncounterIds));
// 转换为DTO
List<BillListDto> records = convertToDtoList(billList);
// 汇总:查询全部匹配记录(不受分页限制)
Map<String, Object> summary = computeBillSummary(billType, payStatus, startTime, endTime,
searchPatientIds, searchEncounterIds, total);
Map<String, Object> result = new HashMap<>();
result.put("records", records);
result.put("total", total);
result.put("summary", summary);
return result;
}
/**
* 将账单实体列表转换为DTO列表
*/
private List<BillListDto> convertToDtoList(List<PaymentReconciliation> billList) {
List<BillListDto> records = new ArrayList<>();
if (billList.isEmpty()) {
return records;
}
List<Long> patientIds = billList.stream().map(PaymentReconciliation::getPatientId).distinct().collect(Collectors.toList());
List<Long> encounterIds = billList.stream().map(PaymentReconciliation::getEncounterId).distinct().collect(Collectors.toList());
List<Long> entererIds = billList.stream().map(PaymentReconciliation::getEntererId).distinct().collect(Collectors.toList());
Map<Long, Patient> patientMap = listToMap(iPatientService.listByIds(patientIds), Patient::getId);
Map<Long, Encounter> encounterMap = listToMap(iEncounterService.listByIds(encounterIds), Encounter::getId);
Map<Long, Practitioner> practitionerMap = listToMap(iPractitionerService.listByIds(entererIds), Practitioner::getId);
// 查询当前页账单的退费金额
List<Long> paymentIds = billList.stream().map(PaymentReconciliation::getId).collect(Collectors.toList());
Map<Long, BigDecimal> refundMap = new HashMap<>();
if (!paymentIds.isEmpty()) {
List<PaymentReconciliation> refunds = paymentReconciliationService.list(
new LambdaQueryWrapper<PaymentReconciliation>()
.in(PaymentReconciliation::getRelationId, paymentIds)
.eq(PaymentReconciliation::getDeleteFlag, DelFlag.NO.getCode())
.in(PaymentReconciliation::getStatusEnum,
PaymentStatus.REFUND_ALL.getValue(), PaymentStatus.REFUND_PART.getValue()));
for (PaymentReconciliation refund : refunds) {
refundMap.merge(refund.getRelationId(), refund.getTenderedAmount(), BigDecimal::add);
}
}
for (PaymentReconciliation bill : billList) {
BillListDto dto = new BillListDto();
dto.setId(bill.getId());
dto.setBillNo(bill.getPaymentNo());
dto.setPatientId(bill.getPatientId());
dto.setEncounterId(bill.getEncounterId());
dto.setTotalAmount(bill.getTenderedAmount() != null ? bill.getTenderedAmount() : BigDecimal.ZERO);
dto.setRefundAmount(refundMap.getOrDefault(bill.getId(), BigDecimal.ZERO));
dto.setPayStatus(bill.getStatusEnum());
dto.setOperatorId(bill.getEntererId());
dto.setPayTime(bill.getBillDate());
dto.setContractNo(bill.getContractNo());
Patient patient = patientMap.get(bill.getPatientId());
if (patient != null) {
dto.setPatientName(patient.getName());
dto.setGenderEnum(patient.getGenderEnum());
dto.setBirthDate(patient.getBirthDate());
if (patient.getBirthDate() != null) {
dto.setAge(AgeCalculatorUtil.calculateAge(patient.getBirthDate()));
}
}
Encounter encounter = encounterMap.get(bill.getEncounterId());
if (encounter != null) {
dto.setEncounterNo(encounter.getBusNo());
}
Practitioner practitioner = practitionerMap.get(bill.getEntererId());
if (practitioner != null) {
dto.setOperatorName(practitioner.getName());
}
if (bill.getStatusEnum() != null) {
PaymentStatus ps = PaymentStatus.getByValue(bill.getStatusEnum());
dto.setPayStatus_dictText(ps != null ? ps.getInfo() : "");
}
records.add(dto);
}
return records;
}
/**
* 将List转为MapkeyExtractor为key提取函数
*/
private <T> Map<Long, T> listToMap(List<T> list, java.util.function.Function<T, Long> keyExtractor) {
Map<Long, T> map = new HashMap<>();
for (T item : list) {
map.put(keyExtractor.apply(item), item);
}
return map;
}
/**
* 构建查询条件
* @param searchPatientIds 预解析的患者ID列表null表示无searchKey
* @param searchEncounterIds 预解析的就诊ID列表null表示无searchKey
*/
private LambdaQueryWrapper<PaymentReconciliation> buildBillQueryWrapper(
String billType, String payStatus, String startTime, String endTime,
List<Long> searchPatientIds, List<Long> searchEncounterIds) {
LambdaQueryWrapper<PaymentReconciliation> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(PaymentReconciliation::getDeleteFlag, DelFlag.NO.getCode())
.eq(PaymentReconciliation::getKindEnum, PaymentKind.OUTPATIENT_CLINIC.getValue());
if (StringUtils.isNotEmpty(payStatus)) {
int ps = Integer.parseInt(payStatus);
if (ps == 0) {
wrapper.eq(PaymentReconciliation::getStatusEnum, PaymentStatus.DRAFT.getValue());
} else if (ps == 1) {
wrapper.eq(PaymentReconciliation::getStatusEnum, PaymentStatus.SUCCESS.getValue());
} else if (ps == 2) {
wrapper.in(PaymentReconciliation::getStatusEnum,
PaymentStatus.REFUND_ALL.getValue(), PaymentStatus.REFUND_PART.getValue());
}
}
if (StringUtils.isNotEmpty(startTime)) {
wrapper.ge(PaymentReconciliation::getBillDate, startTime);
}
if (StringUtils.isNotEmpty(endTime)) {
wrapper.le(PaymentReconciliation::getBillDate, endTime);
}
// searchKey 过滤使用预解析的ID列表
if (searchPatientIds != null && searchEncounterIds != null) {
boolean hasPatients = !searchPatientIds.isEmpty();
boolean hasEncounters = !searchEncounterIds.isEmpty();
if (hasPatients || hasEncounters) {
wrapper.and(w -> {
if (hasPatients) {
w.or().in(PaymentReconciliation::getPatientId, searchPatientIds);
}
if (hasEncounters) {
w.or().in(PaymentReconciliation::getEncounterId, searchEncounterIds);
}
});
} else {
wrapper.eq(PaymentReconciliation::getId, -1L);
}
}
return wrapper;
}
/**
* 查询全部匹配记录的汇总数据(不受分页限制)
*/
private Map<String, Object> computeBillSummary(String billType, String payStatus, String startTime, String endTime,
List<Long> searchPatientIds, List<Long> searchEncounterIds,
long totalCount) {
Map<String, Object> summary = new HashMap<>();
LambdaQueryWrapper<PaymentReconciliation> wrapper = buildBillQueryWrapper(
billType, payStatus, startTime, endTime, searchPatientIds, searchEncounterIds);
wrapper.select(PaymentReconciliation::getStatusEnum, PaymentReconciliation::getTenderedAmount);
List<PaymentReconciliation> allBills = paymentReconciliationService.list(wrapper);
BigDecimal totalAmount = BigDecimal.ZERO;
BigDecimal refundAmount = BigDecimal.ZERO;
for (PaymentReconciliation bill : allBills) {
BigDecimal amount = bill.getTenderedAmount() != null ? bill.getTenderedAmount() : BigDecimal.ZERO;
Integer status = bill.getStatusEnum();
if (PaymentStatus.SUCCESS.getValue().equals(status)) {
totalAmount = totalAmount.add(amount);
} else if (PaymentStatus.REFUND_ALL.getValue().equals(status)
|| PaymentStatus.REFUND_PART.getValue().equals(status)) {
refundAmount = refundAmount.add(amount);
}
}
summary.put("totalAmount", totalAmount);
summary.put("refundAmount", refundAmount);
summary.put("actualAmount", totalAmount.subtract(refundAmount));
summary.put("totalCount", totalCount);
return summary;
}
} }

View File

@@ -72,4 +72,11 @@ public class IChargeBillServiceImpl implements IChargeBillService {
public R<?> checkYbNo() { public R<?> checkYbNo() {
return chargeBillStatisticsService.checkYbNo(); return chargeBillStatisticsService.checkYbNo();
} }
@Override
public Map<String, Object> getBillPage(String searchKey, String billType, String payStatus,
String startTime, String endTime, Integer pageNo, Integer pageSize) {
return chargeBillQueryService.getBillPage(searchKey, billType, payStatus,
startTime, endTime, pageNo, pageSize);
}
} }

View File

@@ -115,4 +115,27 @@ public class ChargeBillController {
return iChargeBillService.checkYbNo(); return iChargeBillService.checkYbNo();
} }
/**
* 分页查询收费账单列表
*/
@GetMapping("/page")
public R<?> page(@RequestParam(value = "searchKey", required = false) String searchKey,
@RequestParam(value = "billType", required = false) String billType,
@RequestParam(value = "payStatus", required = false) String payStatus,
@RequestParam(value = "startTime", required = false) String startTime,
@RequestParam(value = "endTime", required = false) String endTime,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "10") Integer pageSize) {
return R.ok(iChargeBillService.getBillPage(searchKey, billType, payStatus,
startTime, endTime, pageNo, pageSize));
}
/**
* 获取收费详情按ID
*/
@GetMapping("/{id}")
public R<?> getById(@PathVariable Long id) {
return R.ok(iChargeBillService.getDetail(id));
}
} }

View File

@@ -0,0 +1,78 @@
package com.healthlink.his.web.paymentmanage.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.math.BigDecimal;
import java.util.Date;
/**
* 收费账单列表DTO
*/
@Data
@Accessors(chain = true)
public class BillListDto {
/** 账单ID */
private Long id;
/** 账单号 */
private String billNo;
/** 患者ID */
private Long patientId;
/** 患者姓名 */
private String patientName;
/** 性别枚举 */
private Integer genderEnum;
/** 性别文本 */
private String genderEnum_enumText;
/** 年龄 */
private Integer age;
/** 出生日期 */
private Date birthDate;
/** 就诊ID */
private Long encounterId;
/** 门诊号 */
private String encounterNo;
/** 收费类型枚举 */
private Integer billType;
/** 收费类型文本 */
private String billType_dictText;
/** 收费金额 */
private BigDecimal totalAmount;
/** 退费金额 */
private BigDecimal refundAmount;
/** 收费状态枚举 */
private Integer payStatus;
/** 收费状态文本 */
private String payStatus_dictText;
/** 支付方式文本 */
private String payMethod_dictText;
/** 收费员ID */
private Long operatorId;
/** 收费员姓名 */
private String operatorName;
/** 收费时间 */
private Date payTime;
/** 合同编号 */
private String contractNo;
}

View File

@@ -0,0 +1,68 @@
-- Bug #818: TPR变更体温单下拉框无数据 — 补充缺失的字典类型和字典数据
-- 涉及的字典: temperature_select_type, breathe_unit, measurement_select_type,
-- urination_frequency_unit, stoolfrequency_unit
-- ============================================================
-- 1. 插入字典类型 (sys_dict_type)
-- ============================================================
INSERT INTO sys_dict_type (dict_id, dict_name, dict_type, status, create_by, create_time, remark)
VALUES
(543, '体温测量类型', 'temperature_select_type', '0', 'admin', NOW(), 'TPR体温单-体温下拉选项'),
(544, '呼吸类型', 'breathe_unit', '0', 'admin', NOW(), 'TPR体温单-呼吸下拉选项'),
(545, '测量特殊状态', 'measurement_select_type', '0', 'admin', NOW(), 'TPR体温单-体重/腹围/身高特殊状态'),
(546, '小便次数符号', 'urination_frequency_unit', '0', 'admin', NOW(), 'TPR体温单-小便次数特殊符号'),
(547, '大便次数符号', 'stoolfrequency_unit', '0', 'admin', NOW(), 'TPR体温单-大便次数特殊符号')
ON CONFLICT DO NOTHING;
-- ============================================================
-- 2. 插入字典数据 (sys_dict_data)
-- ============================================================
-- temperature_select_type: 腋温/口温/额温/耳温/肛温 + 拒测/外出/请假
-- Codes 1-5 = measurement types (do NOT replace temperature value)
-- Codes 6-8 = special statuses (replace temperature value with text)
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time)
VALUES
(4142, 1, '腋温', '1', 'temperature_select_type', '', 'default', 'Y', '0', 'admin', NOW()),
(4143, 2, '口温', '2', 'temperature_select_type', '', 'default', 'N', '0', 'admin', NOW()),
(4144, 3, '额温', '3', 'temperature_select_type', '', 'default', 'N', '0', 'admin', NOW()),
(4145, 4, '耳温', '4', 'temperature_select_type', '', 'default', 'N', '0', 'admin', NOW()),
(4146, 5, '肛温', '5', 'temperature_select_type', '', 'default', 'N', '0', 'admin', NOW()),
(4147, 6, '拒测', '6', 'temperature_select_type', '', 'danger', 'N', '0', 'admin', NOW()),
(4148, 7, '外出', '7', 'temperature_select_type', '', 'warning', 'N', '0', 'admin', NOW()),
(4149, 8, '请假', '8', 'temperature_select_type', '', 'info', 'N', '0', 'admin', NOW())
ON CONFLICT DO NOTHING;
-- breathe_unit: 平稳/急促/浅慢/叹息样呼吸/潮式呼吸/抑制呼吸/@
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time)
VALUES
(4150, 1, '平稳', '1', 'breathe_unit', '', 'success', 'Y', '0', 'admin', NOW()),
(4151, 2, '急促', '2', 'breathe_unit', '', 'danger', 'N', '0', 'admin', NOW()),
(4152, 3, '浅慢', '3', 'breathe_unit', '', 'warning', 'N', '0', 'admin', NOW()),
(4153, 4, '叹息样呼吸', '4', 'breathe_unit', '', 'info', 'N', '0', 'admin', NOW()),
(4154, 5, '潮式呼吸', '5', 'breathe_unit', '', 'info', 'N', '0', 'admin', NOW()),
(4155, 6, '抑制呼吸', '6', 'breathe_unit', '', 'danger', 'N', '0', 'admin', NOW()),
(4156, 7, '@', '7', 'breathe_unit', '', 'default', 'N', '0', 'admin', NOW())
ON CONFLICT DO NOTHING;
-- measurement_select_type: 卧床/未测
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time)
VALUES
(4157, 1, '卧床', '1', 'measurement_select_type', '', 'warning', 'N', '0', 'admin', NOW()),
(4158, 2, '未测', '2', 'measurement_select_type', '', 'info', 'Y', '0', 'admin', NOW())
ON CONFLICT DO NOTHING;
-- urination_frequency_unit: ※/C
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time)
VALUES
(4159, 1, '', '1', 'urination_frequency_unit', '', 'default', 'N', '0', 'admin', NOW()),
(4160, 2, 'C', '2', 'urination_frequency_unit', '', 'default', 'N', '0', 'admin', NOW())
ON CONFLICT DO NOTHING;
-- stoolfrequency_unit: ※/☆/人工肛门
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time)
VALUES
(4161, 1, '', '1', 'stoolfrequency_unit', '', 'default', 'N', '0', 'admin', NOW()),
(4162, 2, '', '2', 'stoolfrequency_unit', '', 'default', 'N', '0', 'admin', NOW()),
(4163, 3, '人工肛门', '3', 'stoolfrequency_unit', '', 'default', 'N', '0', 'admin', NOW())
ON CONFLICT DO NOTHING;

View File

@@ -0,0 +1,76 @@
-- Bug #818: TPR变更体温单下拉框无数据 — 完整修复
-- 原因: V20260626_2 使用的 dict_code 4142-4163 已被其他字典占用(rate_code/inpatient_diag_category/separate_decocting),
-- dict_id 543-546 也被占用,导致 ON CONFLICT DO NOTHING 跳过所有插入。
-- 本迁移使用全新未占用的 dict_id(548-552) 和 dict_code(4237-4258)。
-- 同时恢复被 V20260626_2 误覆盖的两行原始数据(dict_code 4142→rate_code, 4153→inpatient_diag_category)。
-- ============================================================
-- 1. 恢复被误覆盖的两行原始 dict_type
-- ============================================================
UPDATE sys_dict_data SET dict_type = 'rate_code' WHERE dict_code = 4142;
UPDATE sys_dict_data SET dict_type = 'inpatient_diag_category' WHERE dict_code = 4153;
-- ============================================================
-- 2. 插入字典类型 (sys_dict_type) — dict_id 548-552
-- ============================================================
INSERT INTO sys_dict_type (dict_id, dict_name, dict_type, status, create_by, create_time, remark)
VALUES
(548, '体温测量类型', 'temperature_select_type', '0', 'admin', NOW(), 'TPR体温单-体温下拉选项'),
(549, '呼吸类型', 'breathe_unit', '0', 'admin', NOW(), 'TPR体温单-呼吸下拉选项'),
(550, '测量特殊状态', 'measurement_select_type', '0', 'admin', NOW(), 'TPR体温单-体重/腹围/身高特殊状态'),
(551, '小便次数符号', 'urination_frequency_unit', '0', 'admin', NOW(), 'TPR体温单-小便次数特殊符号'),
(552, '大便次数符号', 'stoolfrequency_unit', '0', 'admin', NOW(), 'TPR体温单-大便次数特殊符号')
ON CONFLICT DO NOTHING;
-- ============================================================
-- 3. 插入字典数据 (sys_dict_data) — dict_code 4237-4258
-- ============================================================
-- temperature_select_type: 腋温/口温/额温/耳温/肛温 + 拒测/外出/请假
-- Codes 1-5 = measurement types (do NOT replace temperature value)
-- Codes 6-8 = special statuses (replace temperature value with text)
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time)
VALUES
(4237, 1, '腋温', '1', 'temperature_select_type', '', 'default', 'Y', '0', 'admin', NOW()),
(4238, 2, '口温', '2', 'temperature_select_type', '', 'default', 'N', '0', 'admin', NOW()),
(4239, 3, '额温', '3', 'temperature_select_type', '', 'default', 'N', '0', 'admin', NOW()),
(4240, 4, '耳温', '4', 'temperature_select_type', '', 'default', 'N', '0', 'admin', NOW()),
(4241, 5, '肛温', '5', 'temperature_select_type', '', 'default', 'N', '0', 'admin', NOW()),
(4242, 6, '拒测', '6', 'temperature_select_type', '', 'danger', 'N', '0', 'admin', NOW()),
(4243, 7, '外出', '7', 'temperature_select_type', '', 'warning', 'N', '0', 'admin', NOW()),
(4244, 8, '请假', '8', 'temperature_select_type', '', 'info', 'N', '0', 'admin', NOW())
ON CONFLICT DO NOTHING;
-- breathe_unit: 平稳/急促/浅慢/叹息样呼吸/潮式呼吸/抑制呼吸/@
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time)
VALUES
(4245, 1, '平稳', '1', 'breathe_unit', '', 'success', 'Y', '0', 'admin', NOW()),
(4246, 2, '急促', '2', 'breathe_unit', '', 'danger', 'N', '0', 'admin', NOW()),
(4247, 3, '浅慢', '3', 'breathe_unit', '', 'warning', 'N', '0', 'admin', NOW()),
(4248, 4, '叹息样呼吸', '4', 'breathe_unit', '', 'info', 'N', '0', 'admin', NOW()),
(4249, 5, '潮式呼吸', '5', 'breathe_unit', '', 'info', 'N', '0', 'admin', NOW()),
(4250, 6, '抑制呼吸', '6', 'breathe_unit', '', 'danger', 'N', '0', 'admin', NOW()),
(4251, 7, '@', '7', 'breathe_unit', '', 'default', 'N', '0', 'admin', NOW())
ON CONFLICT DO NOTHING;
-- measurement_select_type: 卧床/未测
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time)
VALUES
(4252, 1, '卧床', '1', 'measurement_select_type', '', 'warning', 'N', '0', 'admin', NOW()),
(4253, 2, '未测', '2', 'measurement_select_type', '', 'info', 'Y', '0', 'admin', NOW())
ON CONFLICT DO NOTHING;
-- urination_frequency_unit: ※/C
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time)
VALUES
(4254, 1, '', '1', 'urination_frequency_unit', '', 'default', 'N', '0', 'admin', NOW()),
(4255, 2, 'C', '2', 'urination_frequency_unit', '', 'default', 'N', '0', 'admin', NOW())
ON CONFLICT DO NOTHING;
-- stoolfrequency_unit: ※/☆/人工肛门
INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time)
VALUES
(4256, 1, '', '1', 'stoolfrequency_unit', '', 'default', 'N', '0', 'admin', NOW()),
(4257, 2, '', '2', 'stoolfrequency_unit', '', 'default', 'N', '0', 'admin', NOW()),
(4258, 3, '人工肛门', '3', 'stoolfrequency_unit', '', 'default', 'N', '0', 'admin', NOW())
ON CONFLICT DO NOTHING;

View File

@@ -0,0 +1,12 @@
-- Bug #806: lab_apply 表增加 encounter_id 列,用于按就诊流水隔离检验申请单
ALTER TABLE lab_apply ADD COLUMN IF NOT EXISTS encounter_id bigint;
-- 对已有数据:尝试通过 visit_no 关联 adm_encounter.bus_no 回填 encounter_id
UPDATE lab_apply la
SET encounter_id = ae.id
FROM adm_encounter ae
WHERE la.visit_no IS NOT NULL
AND la.visit_no <> ''
AND la.visit_no = ae.bus_no
AND la.delete_flag = '0'
AND ae.delete_flag = '0';

View File

@@ -173,13 +173,20 @@
AND T9sr.delete_flag = '0' AND T9sr.delete_flag = '0'
LEFT JOIN wor_service_request AS wsrp ON wsrp.id = wsr.parent_id AND wsrp.delete_flag = '0' LEFT JOIN wor_service_request AS wsrp ON wsrp.id = wsr.parent_id AND wsrp.delete_flag = '0'
WHERE T1.encounter_id = #{encounterId} WHERE T1.encounter_id = #{encounterId}
AND T1.status_enum IN (0 <choose>
, #{planned} <when test="filterStatus != null">
, #{billable} AND T1.status_enum = #{filterStatus}
, #{billed} </when>
, #{refunding} <otherwise>
, #{refunded} AND T1.status_enum IN (0
, #{partRefund}) , #{planned}
, #{billable}
, #{billed}
, #{refunding}
, #{refunded}
, #{partRefund})
</otherwise>
</choose>
AND T1.context_enum != #{register} AND T1.context_enum != #{register}
AND ( AND (
-- 若能关联到请求表,则必须是“已签发”后才允许收费端展示 -- 若能关联到请求表,则必须是“已签发”后才允许收费端展示

View File

@@ -23,9 +23,8 @@
t1.apply_status AS applyStatus, t1.apply_status AS applyStatus,
t1.apply_remark AS applyRemark t1.apply_remark AS applyRemark
FROM lab_apply AS t1 FROM lab_apply AS t1
INNER JOIN adm_encounter AS t3 ON t1.patient_id::bigint = t3.patient_id
WHERE t1.delete_flag = '0' WHERE t1.delete_flag = '0'
AND t3.id = #{encounterId} AND t1.encounter_id = #{encounterId}
ORDER BY t1.apply_time DESC ORDER BY t1.apply_time DESC
</select> </select>

View File

@@ -187,6 +187,6 @@
<if test="orgId != null"> <if test="orgId != null">
AND organization_id = #{orgId} AND organization_id = #{orgId}
</if> </if>
AND tenant_id = 1 <!-- 租户ID若为动态可改为参数传入 --> <!-- 租户过滤由 MyBatis Plus TenantLineInnerInterceptor 自动注入 -->
</select> </select>
</mapper> </mapper>

View File

@@ -323,7 +323,7 @@
T1.performer_check_id, T1.performer_check_id,
T1.reason_text AS reason_text, T1.reason_text AS reason_text,
T1.check_time AS check_time, T1.check_time AS check_time,
T2."name" AS advice_name, COALESCE(T2."name", T1.content_json::jsonb->>'adviceName') AS advice_name,
T2.id AS item_id, T2.id AS item_id,
NULL::varchar AS volume, NULL::varchar AS volume,
NULL::varchar AS lot_number, NULL::varchar AS lot_number,

View File

@@ -11,10 +11,7 @@
SELECT SELECT
a.total_price, a.total_price,
COALESCE(b1.org_id, b2.org_id, b3.org_id, b4.offered_org_id, b5.org_id) AS org_id, COALESCE(b1.org_id, b2.org_id, b3.org_id, b4.offered_org_id, b5.org_id) AS org_id,
--COALESCE(b1.yb_class_enum, b2.yb_class_enum, b3.yb_class_enum) AS yb_class_enum,
b5.yb_type b5.yb_type
-- 添加更多需要的字段
FROM FROM
adm_charge_item a adm_charge_item a
LEFT JOIN LEFT JOIN
@@ -42,4 +39,5 @@
</foreach> </foreach>
</if> </if>
</select> </select>
</mapper> </mapper>

View File

@@ -124,6 +124,11 @@ public class InspectionLabApply extends HisBaseEntity {
* 备注 * 备注
*/ */
private String applyRemark; private String applyRemark;
/**
* 就诊ID本次就诊流水用于数据隔离
*/
@JsonSerialize(using = ToStringSerializer.class)
private Long encounterId;
/** /**
* 操作员工号 * 操作员工号
*/ */

View File

@@ -14,9 +14,13 @@ export function getList(queryParams) {
/** /**
* 患者处方列表 * 患者处方列表
*/ */
export function getChargeList(encounterId, config = {}) { export function getChargeList(encounterId, statusEnum, config = {}) {
let url = '/charge-manage/charge/patient-prescription?encounterId=' + encounterId
if (statusEnum !== undefined && statusEnum !== null) {
url += '&statusEnum=' + statusEnum
}
return request({ return request({
url: '/charge-manage/charge/patient-prescription?encounterId=' + encounterId, url: url,
method: 'get', method: 'get',
...config ...config
}) })

View File

@@ -468,9 +468,11 @@ function handleSelectionChange(selection) {
} }
function handleTotalAmount() { function handleTotalAmount() {
if (selectedRows.value.length == 0) { if (selectedRows.value.length == 0) {
totalAmounts.value = chargeList.value.reduce((accumulator, currentRow) => { totalAmounts.value = chargeList.value
return new Decimal(accumulator).add(currentRow.totalPrice.toFixed(2) || 0); .filter((row) => row.statusEnum === 1)
}, new Decimal(0)); .reduce((accumulator, currentRow) => {
return new Decimal(accumulator).add(currentRow.totalPrice.toFixed(2) || 0);
}, new Decimal(0));
} else { } else {
totalAmounts.value = selectedRows.value.reduce((accumulator, currentRow) => { totalAmounts.value = selectedRows.value.reduce((accumulator, currentRow) => {
return new Decimal(accumulator).add(currentRow.totalPrice.toFixed(2) || 0); return new Decimal(accumulator).add(currentRow.totalPrice.toFixed(2) || 0);
@@ -532,7 +534,7 @@ function clickRow(params) {
patientInfo.value = { ...row, encounterId: encId }; patientInfo.value = { ...row, encounterId: encId };
chargeLoading.value = true; chargeLoading.value = true;
encounterId.value = encId; encounterId.value = encId;
getChargeList(encId).then((res) => { getChargeList(encId, queryParams.value.statusEnum).then((res) => {
chargeList.value = res.data; chargeList.value = res.data;
setTimeout(() => { setTimeout(() => {
chargeLoading.value = false; chargeLoading.value = false;
@@ -546,7 +548,7 @@ function handleClose(value, msg) {
if (value == 'success') { if (value == 'success') {
proxy.$modal.msgSuccess(msg); proxy.$modal.msgSuccess(msg);
chargeLoading.value = true; chargeLoading.value = true;
getChargeList(patientInfo.value.encounterId, { skipErrorMsg: true }).then((res) => { getChargeList(patientInfo.value.encounterId, queryParams.value.statusEnum, { skipErrorMsg: true }).then((res) => {
chargeList.value = res.data; chargeList.value = res.data;
setTimeout(() => { setTimeout(() => {
chargeLoading.value = false; chargeLoading.value = false;

View File

@@ -2003,16 +2003,16 @@ const formatAmount = (amount) => {
// 单据状态标签文字 // 单据状态标签文字
const getStatusLabel = (applyStatus, row) => { const getStatusLabel = (applyStatus, row) => {
// applyStatus: 0=待开立, 1=已开立(已签发) // applyStatus: 0=待签发, 1=已签发
// 结合收费/执行标记推导更丰富的状态 // 结合收费/执行标记推导更丰富的状态
if (applyStatus === 1) { if (applyStatus === 1) {
// 已收费后根据执行标记判断 // 已收费后根据执行标记判断
if (row.needExecute === true) { if (row.needExecute === true) {
return '已执行' return '已执行'
} }
return '已开立' return '已签发'
} }
return '待开立' return '待签发'
} }
// 单据状态标签颜色 // 单据状态标签颜色

View File

@@ -485,10 +485,8 @@ const handleEditSubmit = () => {
const openAct = () => { const openAct = () => {
console.log(props.patientInfo, 'patientRegister.vue'); console.log(props.patientInfo, 'patientRegister.vue');
console.log(props.inHospitalInfo, 'inHospitalInfo.vue'); console.log(props.inHospitalInfo, 'inHospitalInfo.vue');
/* 初始化数据 */
advance.value = props.inHospitalInfo.balanceAmount || 0; advance.value = props.inHospitalInfo.balanceAmount || 0;
advancePaymentVisible.value = false; advancePaymentVisible.value = false;
RegisterFormRef.value.init();
}; };
const closedAct = () => { const closedAct = () => {
dialogVisible.value = false; dialogVisible.value = false;

View File

@@ -312,11 +312,12 @@ const props = defineProps({
}); });
const organization = ref([]); const organization = ref([]);
const fullOrgTree = ref([]); // 未过滤的完整组织树(保留 children 层级)
const wardListOptions = ref([]); const wardListOptions = ref([]);
const practitionerWardList = ref([]); // 当前用户有权限的病区(与护士站同源)
const contractList = ref([]); const contractList = ref([]);
const verificationStatusOptions = ref([]); const verificationStatusOptions = ref([]);
const diagnosisDefinitionList = ref([]); const diagnosisDefinitionList = ref([]);
const wardLoading = ref(false);
const rules = reactive({ const rules = reactive({
inHospitalOrgId: [ inHospitalOrgId: [
{ {
@@ -451,17 +452,6 @@ watch(
}, },
{ immediate: true } { immediate: true }
); );
watch(
() => props.isRegistered,
(newVal) => {
if (newVal) {
// 已登记状态可以做一些数据锁定操作
} else {
init();
}
}
);
onMounted(async () => { onMounted(async () => {
await getInitOptions(); await getInitOptions();
setValue(); setValue();
@@ -501,15 +491,21 @@ function handleWardClick(item) {
function getInitOptions() { function getInitOptions() {
// 获取所有科室 // 获取所有科室
const orgPromise = getOrgList(); const orgPromise = getOrgList();
// 获取所有病区 // 获取当前用户有权限的病区(与护士站入出管理同款数据源)
const wardPromise = getPractitionerWard(); const wardPromise = getPractitionerWard();
const initPromise = Promise.all([orgPromise, wardPromise]).then(([orgRes, wardRes]) => { const initPromise = Promise.all([orgPromise, wardPromise]).then(([orgRes, wardRes]) => {
// 保存完整组织树(含 children 层级),用于 findOrgNode 递归查找
fullOrgTree.value = orgRes.data.records || [];
// 入院科室:展示所有 typeEnum=2(科室) + classEnum含"2"(住院) 的科室 // 入院科室:展示所有 typeEnum=2(科室) + classEnum含"2"(住院) 的科室
organization.value = orgRes.data.records.filter( organization.value = orgRes.data.records.filter(
(record) => record.typeEnum === 2 && checkClassEnumValue(record.classEnum, 2) (record) => record.typeEnum === 2 && checkClassEnumValue(record.classEnum, 2)
); );
// 保存当前用户有权限的病区列表(与护士站使用相同数据源)
practitionerWardList.value = wardRes.data || [];
// Bug #178 Fix: 如果已选科室不在列表中,手动添加以确保正确显示 // Bug #178 Fix: 如果已选科室不在列表中,手动添加以确保正确显示
const selectedOrgId = props.inHospitalInfo?.inHospitalOrgId; const selectedOrgId = props.inHospitalInfo?.inHospitalOrgId;
const selectedOrgName = props.inHospitalInfo?.inHospitalOrgName; const selectedOrgName = props.inHospitalInfo?.inHospitalOrgName;
@@ -545,39 +541,43 @@ function getDiagnosisInfo(value) {
} }
function handleNodeClick(orgInfo) { function handleNodeClick(orgInfo) {
const savedWardId = props.inHospitalInfo?.wardLocationId; // 保存原始病区ID用于编辑模式恢复 const savedWardId = props.inHospitalInfo?.wardLocationId;
submitForm.wardLocationId = undefined; // 切换科室时,先清空原有病区 submitForm.wardLocationId = undefined;
submitForm.totalBedsNum = undefined; submitForm.totalBedsNum = undefined;
submitForm.idleBedsNum = undefined; submitForm.idleBedsNum = undefined;
wardListOptions.value = []; // 先清空列表,避免旧数据残留 wardListOptions.value = [];
wardLoading.value = true;
wardList({ orgId: orgInfo.id }) if (orgInfo.id) {
.then((res) => { // 后端查询:自动包含子孙科室的病区(通过 busNo 层级匹配)
wardListOptions.value = res.data || []; wardList({ orgId: orgInfo.id })
if (wardListOptions.value.length > 0) { .then((res) => {
// 编辑模式:尝试恢复之前保存的病区 wardListOptions.value = res.data || [];
if (savedWardId) { selectSavedOrFirstWard(savedWardId);
const savedWard = wardListOptions.value.find((item) => String(item.id) === String(savedWardId)); })
if (savedWard) { .catch(() => {
submitForm.wardLocationId = savedWardId; wardListOptions.value = [];
handleWardClick(savedWard); });
return; } else {
} wardListOptions.value = practitionerWardList.value || [];
} selectSavedOrFirstWard(savedWardId);
// 新增模式 或 原病区不在新科室下:自动选中第一个病区 }
if (!submitForm.wardLocationId) { }
submitForm.wardLocationId = wardListOptions.value[0].id;
handleWardClick(wardListOptions.value[0]); function selectSavedOrFirstWard(savedWardId) {
} if (wardListOptions.value.length > 0) {
if (savedWardId) {
const savedWard = wardListOptions.value.find((item) => String(item.id) === String(savedWardId));
if (savedWard) {
submitForm.wardLocationId = savedWardId;
handleWardClick(savedWard);
return;
} }
}) }
.catch((err) => { if (!submitForm.wardLocationId) {
console.error('加载病区失败:', err); submitForm.wardLocationId = wardListOptions.value[0].id;
wardListOptions.value = []; handleWardClick(wardListOptions.value[0]);
}) }
.finally(() => { }
wardLoading.value = false;
});
} }
function handleChange(value) { function handleChange(value) {
@@ -586,7 +586,6 @@ function handleChange(value) {
submitForm.wardLocationId = undefined; submitForm.wardLocationId = undefined;
submitForm.totalBedsNum = undefined; submitForm.totalBedsNum = undefined;
submitForm.idleBedsNum = undefined; submitForm.idleBedsNum = undefined;
wardLoading.value = false; // 同步停止加载
} }
} }
@@ -649,14 +648,6 @@ const validateData = async (callback) => {
}); });
}; };
const init = () => {
if (!props.isRegistered) {
// 只有待登记状态才重置
submitForm.inDocterWorkGroupCode = '';
submitForm.wardLocationId = '';
}
};
// 检查classEnum值是否包含指定值支持多选 // 检查classEnum值是否包含指定值支持多选
function checkClassEnumValue(classEnum, targetValue) { function checkClassEnumValue(classEnum, targetValue) {
if (!classEnum) return false; if (!classEnum) return false;
@@ -671,7 +662,7 @@ function checkClassEnumValue(classEnum, targetValue) {
return classEnum == targetValue; return classEnum == targetValue;
} }
defineExpose({ validateData, submitForm, init, medicalInsuranceTitle }); defineExpose({ validateData, submitForm, medicalInsuranceTitle });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.registerForm-container { .registerForm-container {

View File

@@ -1,7 +1,7 @@
<template> <template>
<el-dialog <el-dialog
:model-value="visible" :model-value="visible"
:title="t('module.diagnosis.title.tcmDiagnosis')" title="中医诊断"
:width="width" :width="width"
:z-index="20" :z-index="20"
teleported teleported
@@ -17,12 +17,12 @@
label-width="100px" label-width="100px"
> >
<el-form-item <el-form-item
:label="t('module.diagnosis.title.tcmDiagnosis')" label="中医诊断"
prop="conditionCode" prop="conditionCode"
> >
<el-select <el-select
v-model="formData.conditionCode" v-model="formData.conditionCode"
:placeholder="t('module.diagnosis.placeholder.selectTcmDiagnosis')" placeholder="请选择中医诊断"
filterable filterable
clearable clearable
style="width: 100%" style="width: 100%"
@@ -37,21 +37,23 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
:label="t('module.diagnosis.label.tcmSyndrome')" label="中医证候"
prop="syndromeCode" prop="syndromeCode"
> >
<el-cascader <el-select
ref="cascaderRef"
v-model="formData.syndromeCode" v-model="formData.syndromeCode"
:options="getGroupedSyndromeOptions(syndromeOptions)" placeholder="请选择中医证候"
:props="{ emitPath: false, checkStrictly: true }"
:show-all-levels="false"
:placeholder="t('module.diagnosis.placeholder.selectTcmSyndrome')"
filterable filterable
clearable clearable
style="width: 100%" style="width: 100%"
@change="handleSyndromeCodeChange" >
/> <el-option
v-for="item in syndromeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
@@ -76,14 +78,11 @@
import {onMounted, reactive, ref} from 'vue' import {onMounted, reactive, ref} from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { getTcmCondition, getTcmSyndrome, saveTcmDiagnosis } from '../api' import { getTcmCondition, getTcmSyndrome, saveTcmDiagnosis } from '../api'
import { useI18n } from 'vue-i18n'
const { proxy } = getCurrentInstance() const { proxy } = getCurrentInstance()
const { t } = useI18n()
const conditionOptions = ref([]) const conditionOptions = ref([])
const syndromeOptions = ref([]) const syndromeOptions = ref([])
const cascaderRef = ref(null)
const formData = ref({ const formData = ref({
conditionCode: '', conditionCode: '',
@@ -160,14 +159,14 @@ const handleSubmit = async () => {
}] }]
saveTcmDiagnosis(submitData).then((res) => { saveTcmDiagnosis(submitData).then((res) => {
if (res.code === 200) { if (res.code === 200) {
ElMessage.success(t('inpatientDoctor.diagnosis.tcmSaveSuccess')) ElMessage.success('中医诊断保存成功')
emit('ok-act') emit('ok-act')
cancelAct() cancelAct()
} else { } else {
ElMessage.error(res.msg || t('inpatientDoctor.diagnosis.saveFailed')) ElMessage.error(res.msg || '保存失败')
} }
}).catch(() => { }).catch(() => {
ElMessage.error(t('inpatientDoctor.diagnosis.saveFailedRetry')) ElMessage.error('保存失败,请重试')
}) })
} }
}) })
@@ -178,154 +177,6 @@ const openAct = () => {
loadConditionOptions() loadConditionOptions()
loadSyndromeOptions() loadSyndromeOptions()
} }
function getGroupedSyndromeOptions(options) {
if (!options || !options.length) return []
const seen = new Set()
const uniqueOptions = []
options.forEach(item => {
if (!seen.has(item.label)) {
seen.add(item.label)
uniqueOptions.push(item)
}
})
const nameToItems = {}
uniqueOptions.forEach(item => {
const name = item.label
if (!nameToItems[name]) {
nameToItems[name] = []
}
nameToItems[name].push(item)
})
// Level 3 Children of 阴证 (寒证, 虚证)
const childrenOfYin = []
;['寒证', '虚证'].forEach(name => {
if (nameToItems[name]) {
childrenOfYin.push(...nameToItems[name])
}
})
// Level 3 Children of 阳证 (热证, 实证)
const childrenOfYang = []
;['热证', '实证'].forEach(name => {
if (nameToItems[name]) {
childrenOfYang.push(...nameToItems[name])
}
})
// Level 2 under 八纲总纲 (阴证, 阳证)
const level2OfBagang = []
if (nameToItems['阴证']) {
nameToItems['阴证'].forEach(item => {
level2OfBagang.push({
value: item.value,
label: item.label,
id: item.id,
children: childrenOfYin.length ? childrenOfYin : undefined
})
})
} else if (childrenOfYin.length > 0) {
level2OfBagang.push({
value: 'virtual-yin',
label: '阴证',
children: childrenOfYin
})
}
if (nameToItems['阳证']) {
nameToItems['阳证'].forEach(item => {
level2OfBagang.push({
value: item.value,
label: item.label,
id: item.id,
children: childrenOfYang.length ? childrenOfYang : undefined
})
})
} else if (childrenOfYang.length > 0) {
level2OfBagang.push({
value: 'virtual-yang',
label: '阳证',
children: childrenOfYang
})
}
// Level 2 under 危重急症 (闭证, 脱证)
const level2OfWeizhong = []
;['闭证', '脱证'].forEach(name => {
if (nameToItems[name]) {
level2OfWeizhong.push(...nameToItems[name])
}
})
// Level 2 under 其他证候 (all other syndromes)
const level2OfOther = []
const specialNames = new Set(['阴证', '阳证', '寒证', '热证', '虚证', '实证', '闭证', '脱证'])
options.forEach(item => {
if (!specialNames.has(item.label)) {
level2OfOther.push(item)
}
})
const finalTree = []
if (level2OfBagang.length > 0) {
finalTree.push({
value: 'root-bagang',
label: '八纲总纲',
children: level2OfBagang
})
}
if (level2OfWeizhong.length > 0) {
finalTree.push({
value: 'root-weizhong',
label: '危重急症',
children: level2OfWeizhong
})
}
if (level2OfOther.length > 0) {
finalTree.push({
value: 'root-other',
label: '其他证候',
children: level2OfOther
})
}
return finalTree
}
function handleSyndromeCodeChange(val) {
console.log('handleSyndromeCodeChange called with:', val)
if (val === 'root-bagang' || val === 'root-weizhong' || val === 'root-other' || (typeof val === 'string' && val.startsWith('virtual-'))) {
formData.value.syndromeCode = ''
} else if (val) {
if (cascaderRef.value) {
console.log('Cascader Ref component instance:', cascaderRef.value)
if (typeof cascaderRef.value.togglePopperVisible === 'function') {
cascaderRef.value.togglePopperVisible(false)
}
if (typeof cascaderRef.value.toggleDropDownVisible === 'function') {
cascaderRef.value.toggleDropDownVisible(false)
}
cascaderRef.value.popperVisible = false
if (typeof cascaderRef.value.blur === 'function') {
cascaderRef.value.blur()
}
}
if (document.activeElement && typeof document.activeElement.blur === 'function') {
console.log('Blurring active element:', document.activeElement)
document.activeElement.blur()
}
setTimeout(() => {
document.body.click()
}, 50)
}
}
const closedAct = () => { const closedAct = () => {
emit('update:visible', false) emit('update:visible', false)
} }

View File

@@ -7,7 +7,7 @@
> >
<el-input <el-input
v-model="diagnosis" v-model="diagnosis"
:placeholder="t('module.diagnosis.placeholder.diagnosisName')" placeholder="诊断名称"
clearable clearable
style="width: 100%; margin-bottom: 10px" style="width: 100%; margin-bottom: 10px"
@keyup.enter="queryDiagnosisUse" @keyup.enter="queryDiagnosisUse"
@@ -53,7 +53,7 @@
" "
width="200" width="200"
:hide-after="10" :hide-after="10"
:title="t('module.diagnosis.title.confirmDeleteDiagnosis')" title="确认删除此常用诊断吗"
placement="top-start" placement="top-start"
@confirm="deleteChild(data)" @confirm="deleteChild(data)"
> >
@@ -123,7 +123,7 @@
height="650" height="650"
> >
<vxe-column <vxe-column
:title="t('inpatientDoctor.diagnosis.seqNo')" title="序号"
width="50" width="50"
> >
<template #default="scope"> <template #default="scope">
@@ -131,7 +131,7 @@
</template> </template>
</vxe-column> </vxe-column>
<vxe-column <vxe-column
:title="t('inpatientDoctor.diagnosis.diagnosisSort')" title="诊断排序"
align="center" align="center"
field="diagSrtNo" field="diagSrtNo"
width="120" width="120"
@@ -151,7 +151,7 @@
</template> </template>
</vxe-column> </vxe-column>
<vxe-column <vxe-column
:title="t('inpatientDoctor.diagnosis.diagnosisSystem')" title="诊断体系"
align="center" align="center"
field="diagnosisSystem" field="diagnosisSystem"
width="120" width="120"
@@ -167,11 +167,11 @@
@change="handleDiagnosisSystemChange(scope.row)" @change="handleDiagnosisSystemChange(scope.row)"
> >
<el-option <el-option
:label="t('inpatientDoctor.diagnosis.western')" label="西医"
value="西医" value="西医"
/> />
<el-option <el-option
:label="t('inpatientDoctor.diagnosis.chinese')" label="中医"
value="中医" value="中医"
/> />
</el-select> </el-select>
@@ -179,7 +179,7 @@
</template> </template>
</vxe-column> </vxe-column>
<vxe-column <vxe-column
:title="t('inpatientDoctor.diagnosis.diagnosisCategory')" title="诊断类别"
align="center" align="center"
field="medTypeCode" field="medTypeCode"
width="160" width="160"
@@ -205,7 +205,7 @@
</template> </template>
</vxe-column> </vxe-column>
<vxe-column <vxe-column
:title="t('inpatientDoctor.diagnosis.diagnosisName')" title="诊断名称"
align="center" align="center"
field="name" field="name"
min-width="180" min-width="180"
@@ -231,7 +231,7 @@
<template #reference> <template #reference>
<el-input <el-input
v-model="scope.row.name" v-model="scope.row.name"
:placeholder="t('module.diagnosis.placeholder.selectDiagnosis')" placeholder="请选择诊断"
@input="handleChange" @input="handleChange"
@focus="handleFocus(scope.row, scope.rowIndex)" @focus="handleFocus(scope.row, scope.rowIndex)"
@blur="handleBlur(scope.row)" @blur="handleBlur(scope.row)"
@@ -243,7 +243,7 @@
</vxe-column> </vxe-column>
<vxe-column <vxe-column
:title="t('inpatientDoctor.diagnosis.diagnosisDoctor')" title="诊断医生"
align="center" align="center"
field="diagnosisDoctor" field="diagnosisDoctor"
width="120" width="120"
@@ -268,19 +268,22 @@
:prop="`diagnosisList.${scope.rowIndex}.tcmSyndromeCode`" :prop="`diagnosisList.${scope.rowIndex}.tcmSyndromeCode`"
:rules="scope.row.diagnosisSystem === '中医' ? [{ required: true, message: '请选择中医证候', trigger: 'change' }] : []" :rules="scope.row.diagnosisSystem === '中医' ? [{ required: true, message: '请选择中医证候', trigger: 'change' }] : []"
> >
<el-cascader <el-select
:ref="el => setCascaderRef(el, scope.rowIndex)"
v-model="scope.row.tcmSyndromeCode" v-model="scope.row.tcmSyndromeCode"
:options="getGroupedSyndromeOptions(scope.row.syndromeOptions)" placeholder="请选择中医证候"
:props="{ emitPath: false, checkStrictly: true }"
:show-all-levels="false"
:placeholder="t('module.diagnosis.placeholder.selectTcmSyndrome')"
filterable filterable
clearable clearable
style="width: 100%" style="width: 100%"
@focus="loadSyndromeOptions(scope.row)" @focus="loadSyndromeOptions(scope.row)"
@change="(val) => handleSyndromeSelect(val, scope.row, scope.rowIndex)" @change="(val) => handleSyndromeSelect(val, scope.row)"
/> >
<el-option
v-for="item in scope.row.syndromeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item> </el-form-item>
</template> </template>
<el-form-item v-else> <el-form-item v-else>
@@ -289,7 +292,7 @@
</template> </template>
</vxe-column> </vxe-column>
<vxe-column <vxe-column
:title="t('inpatientDoctor.diagnosis.diagnosisTime')" title="诊断时间"
align="center" align="center"
field="diagnosisTime" field="diagnosisTime"
width="180" width="180"
@@ -301,7 +304,7 @@
</template> </template>
</vxe-column> </vxe-column>
<vxe-column <vxe-column
:title="t('inpatientDoctor.diagnosis.diagnosisCode')" title="诊断代码"
align="center" align="center"
field="ybNo" field="ybNo"
width="160" width="160"
@@ -313,7 +316,7 @@
</template> </template>
</vxe-column> </vxe-column>
<vxe-column <vxe-column
:title="t('inpatientDoctor.diagnosis.diagnosisType')" title="诊断类型"
align="center" align="center"
field="maindiseFlag" field="maindiseFlag"
width="170" width="170"
@@ -347,7 +350,7 @@
</template> </template>
</vxe-column> </vxe-column>
<vxe-column <vxe-column
:title="t('common.operation')" title="操作"
align="center" align="center"
width="130" width="130"
> >
@@ -404,9 +407,6 @@ import AddDiagnosisDialog from './addDiagnosisDialog.vue';
import diagnosislist from '../diagnosis/diagnosislist.vue'; import diagnosislist from '../diagnosis/diagnosislist.vue';
import {ElMessage} from 'element-plus'; import {ElMessage} from 'element-plus';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
// const diagnosisList = ref([]); // const diagnosisList = ref([]);
const allowAdd = ref(false); const allowAdd = ref(false);
const isSaving = ref(false); const isSaving = ref(false);
@@ -419,12 +419,6 @@ const rowIndex = ref();
const diagnosis = ref(); const diagnosis = ref();
const orgOrUser = ref(); const orgOrUser = ref();
const syndromeOptions = ref([]); const syndromeOptions = ref([]);
const cascaderRefs = ref({});
const setCascaderRef = (el, index) => {
if (el) {
cascaderRefs.value[index] = el;
}
};
const form = ref({ const form = ref({
diagnosisList: [], diagnosisList: [],
}); });
@@ -641,7 +635,7 @@ function handleImport() {
// 检查是否已填写病历 // 检查是否已填写病历
if (!allowAdd.value) { if (!allowAdd.value) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.fillEmrFirst')); proxy.$modal.msgWarning('请先填写病历');
return; return;
} }
@@ -650,7 +644,7 @@ function handleImport() {
(item) => !item.conditionId && !item.encounterDiagnosisId (item) => !item.conditionId && !item.encounterDiagnosisId
); );
if (hasUnsaved) { if (hasUnsaved) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.saveCurrentDiagnosis')); proxy.$modal.msgWarning('请保存当前诊断');
return; return;
} }
@@ -694,7 +688,7 @@ function addChild(data) {
function deleteChild(data) { function deleteChild(data) {
deleteDiagnosisBind(data.id).then((res) => { deleteDiagnosisBind(data.id).then((res) => {
if (res.code == 200) { if (res.code == 200) {
proxy.$modal.msgSuccess(t('inpatientDoctor.diagnosis.deleteSuccess')); proxy.$modal.msgSuccess('删除成功');
getTree(); getTree();
} }
}); });
@@ -755,7 +749,7 @@ function handleAddDiagnosis() {
// 检查是否已填写病历(必须先于其他检查) // 检查是否已填写病历(必须先于其他检查)
if (!allowAdd.value) { if (!allowAdd.value) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.fillEmrFirst')); proxy.$modal.msgWarning('请先填写病历');
return; return;
} }
@@ -764,7 +758,7 @@ function handleAddDiagnosis() {
(item) => !item.conditionId && !item.encounterDiagnosisId (item) => !item.conditionId && !item.encounterDiagnosisId
); );
if (hasUnsaved) { if (hasUnsaved) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.saveCurrentDiagnosis')); proxy.$modal.msgWarning('请保存当前诊断');
return; return;
} }
@@ -784,7 +778,7 @@ function handleAddDiagnosis() {
); );
if (!valid || hasUnsavedNow) { if (!valid || hasUnsavedNow) {
if (hasUnsavedNow) { if (hasUnsavedNow) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.saveCurrentDiagnosis')); proxy.$modal.msgWarning('请保存当前诊断');
} }
return; return;
} }
@@ -857,43 +851,11 @@ function loadSyndromeOptions(row) {
} }
// 中医证候选中赋值 // 中医证候选中赋值
function handleSyndromeSelect(val, row, rowIndex) { function handleSyndromeSelect(val, row) {
console.log('handleSyndromeSelect called with:', val, 'rowIndex:', rowIndex);
if (val) { if (val) {
if (val === 'root-bagang' || val === 'root-weizhong' || val === 'root-other' || (typeof val === 'string' && val.startsWith('virtual-'))) {
row.tcmSyndromeCode = '';
row.tcmSyndromeName = '';
row.syndromeDefinitionId = '';
return;
}
const selected = (row.syndromeOptions || []).find((item) => item.value === val); const selected = (row.syndromeOptions || []).find((item) => item.value === val);
row.tcmSyndromeName = selected ? selected.label : ''; row.tcmSyndromeName = selected ? selected.label : '';
row.syndromeDefinitionId = selected ? selected.id : ''; row.syndromeDefinitionId = selected ? selected.id : '';
// Close the cascader dropdown programmatically
if (rowIndex !== undefined && cascaderRefs.value[rowIndex]) {
const refEl = cascaderRefs.value[rowIndex];
console.log('Cascader Ref component instance:', refEl);
if (refEl) {
if (typeof refEl.togglePopperVisible === 'function') {
refEl.togglePopperVisible(false);
}
if (typeof refEl.toggleDropDownVisible === 'function') {
refEl.toggleDropDownVisible(false);
}
refEl.popperVisible = false;
if (typeof refEl.blur === 'function') {
refEl.blur();
}
}
}
if (document.activeElement && typeof document.activeElement.blur === 'function') {
console.log('Blurring active element:', document.activeElement);
document.activeElement.blur();
}
setTimeout(() => {
document.body.click();
}, 50);
} else { } else {
row.tcmSyndromeName = ''; row.tcmSyndromeName = '';
row.syndromeDefinitionId = ''; row.syndromeDefinitionId = '';
@@ -954,7 +916,7 @@ function handleMaindise(value, index) {
}); });
if (flag > 1) { if (flag > 1) {
form.value.diagnosisList[index].maindiseFlag = 0; form.value.diagnosisList[index].maindiseFlag = 0;
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.onlyOneMainDiagnosis')); proxy.$modal.msgWarning('只能有一条主诊断');
} }
} }
} }
@@ -990,10 +952,10 @@ function handleSaveDiagnosis() {
proxy.$refs.formRef.validate((valid) => { proxy.$refs.formRef.validate((valid) => {
if (valid) { if (valid) {
if (form.value.diagnosisList.length === 0) { if (form.value.diagnosisList.length === 0) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.diagnosisCannotBeEmpty')); proxy.$modal.msgWarning('诊断不能为空');
return; return;
} else if (!form.value.diagnosisList.some((diagnosis) => diagnosis.maindiseFlag === 1)) { } else if (!form.value.diagnosisList.some((diagnosis) => diagnosis.maindiseFlag === 1)) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.addAtLeastOneMain')); proxy.$modal.msgWarning('至少添加一条主诊断');
return; return;
} }
@@ -1001,11 +963,11 @@ function handleSaveDiagnosis() {
for (let i = 0; i < form.value.diagnosisList.length; i++) { for (let i = 0; i < form.value.diagnosisList.length; i++) {
const item = form.value.diagnosisList[i]; const item = form.value.diagnosisList[i];
if (!item.name) { if (!item.name) {
ElMessage.warning(t('inpatientDoctor.diagnosis.rowNameCannotBeEmpty', { row: i + 1 })); ElMessage.warning(`第${i + 1}行诊断名称不能为空`);
return; return;
} }
if (item.diagnosisSystem === '中医' && !item.tcmSyndromeCode) { if (item.diagnosisSystem === '中医' && !item.tcmSyndromeCode) {
ElMessage.error(t('inpatientDoctor.diagnosis.tcmDiagnosisIncomplete')); ElMessage.error('中医诊断不完整,请录入对应的证候!');
return; return;
} }
} }
@@ -1116,7 +1078,7 @@ function handleSaveDiagnosis() {
// 所有保存完成后刷新 // 所有保存完成后刷新
emits('diagnosisSave', false); emits('diagnosisSave', false);
proxy.$modal.msgSuccess(t('inpatientDoctor.diagnosis.diagnosisSaved')); proxy.$modal.msgSuccess('诊断已保存');
getList(); getList();
// 食源性疾病逻辑 // 食源性疾病逻辑
@@ -1127,7 +1089,7 @@ function handleSaveDiagnosis() {
}); });
} catch (e) { } catch (e) {
console.error('保存诊断失败', e); console.error('保存诊断失败', e);
proxy.$modal.msgError(t('inpatientDoctor.diagnosis.saveDiagnosisFailed')); proxy.$modal.msgError('保存诊断失败');
} finally { } finally {
setTimeout(() => { setTimeout(() => {
isSaving.value = false; isSaving.value = false;
@@ -1142,7 +1104,7 @@ function handleSaveDiagnosis() {
*/ */
function closeDiagnosisDialog(str) { function closeDiagnosisDialog(str) {
if (str === 'success') { if (str === 'success') {
proxy.$modal.msgSuccess(t('inpatientDoctor.order.msg.operationSuccess')); proxy.$modal.msgSuccess('操作成功');
} }
openAddDiagnosisDialog.value = false; openAddDiagnosisDialog.value = false;
@@ -1190,7 +1152,7 @@ function handleNodeClick(data) {
// 检查是否已填写病历 // 检查是否已填写病历
if (!allowAdd.value) { if (!allowAdd.value) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.fillEmrFirst')); proxy.$modal.msgWarning('请先填写病历');
return; return;
} }
@@ -1199,7 +1161,7 @@ function handleNodeClick(data) {
(item) => !item.conditionId && !item.encounterDiagnosisId (item) => !item.conditionId && !item.encounterDiagnosisId
); );
if (hasUnsaved) { if (hasUnsaved) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.saveCurrentDiagnosis')); proxy.$modal.msgWarning('请保存当前诊断');
return; return;
} }
@@ -1207,7 +1169,7 @@ function handleNodeClick(data) {
(diagnosis) => diagnosis.ybNo === data.ybNo || diagnosis.name === data.name (diagnosis) => diagnosis.ybNo === data.ybNo || diagnosis.name === data.name
); );
if (isDuplicate) { if (isDuplicate) {
proxy.$modal.msgWarning(t('inpatientDoctor.diagnosis.diagnosisAlreadyExists')); proxy.$modal.msgWarning('该诊断项已存在');
return; return;
} }
form.value.diagnosisList.push({ form.value.diagnosisList.push({
@@ -1235,126 +1197,6 @@ function handleNodeClick(data) {
} }
} }
function getGroupedSyndromeOptions(options) {
if (!options || !options.length) return [];
const seen = new Set();
const uniqueOptions = [];
options.forEach(item => {
if (!seen.has(item.label)) {
seen.add(item.label);
uniqueOptions.push(item);
}
});
const nameToItems = {};
uniqueOptions.forEach(item => {
const name = item.label;
if (!nameToItems[name]) {
nameToItems[name] = [];
}
nameToItems[name].push(item);
});
// Level 3 Children of 阴证 (寒证, 虚证)
const childrenOfYin = [];
['寒证', '虚证'].forEach(name => {
if (nameToItems[name]) {
childrenOfYin.push(...nameToItems[name]);
}
});
// Level 3 Children of 阳证 (热证, 实证)
const childrenOfYang = [];
['热证', '实证'].forEach(name => {
if (nameToItems[name]) {
childrenOfYang.push(...nameToItems[name]);
}
});
// Level 2 under 八纲总纲 (阴证, 阳证)
const level2OfBagang = [];
if (nameToItems['阴证']) {
nameToItems['阴证'].forEach(item => {
level2OfBagang.push({
value: item.value,
label: item.label,
id: item.id,
children: childrenOfYin.length ? childrenOfYin : undefined
});
});
} else if (childrenOfYin.length > 0) {
level2OfBagang.push({
value: 'virtual-yin',
label: '阴证',
children: childrenOfYin
});
}
if (nameToItems['阳证']) {
nameToItems['阳证'].forEach(item => {
level2OfBagang.push({
value: item.value,
label: item.label,
id: item.id,
children: childrenOfYang.length ? childrenOfYang : undefined
});
});
} else if (childrenOfYang.length > 0) {
level2OfBagang.push({
value: 'virtual-yang',
label: '阳证',
children: childrenOfYang
});
}
// Level 2 under 危重急症 (闭证, 脱证)
const level2OfWeizhong = [];
['闭证', '脱证'].forEach(name => {
if (nameToItems[name]) {
level2OfWeizhong.push(...nameToItems[name]);
}
});
// Level 2 under 其他证候 (all other syndromes)
const level2OfOther = [];
const specialNames = new Set(['阴证', '阳证', '寒证', '热证', '虚证', '实证', '闭证', '脱证']);
options.forEach(item => {
if (!specialNames.has(item.label)) {
level2OfOther.push(item);
}
});
const finalTree = [];
if (level2OfBagang.length > 0) {
finalTree.push({
value: 'root-bagang',
label: '八纲总纲',
children: level2OfBagang
});
}
if (level2OfWeizhong.length > 0) {
finalTree.push({
value: 'root-weizhong',
label: '危重急症',
children: level2OfWeizhong
});
}
if (level2OfOther.length > 0) {
finalTree.push({
value: 'root-other',
label: '其他证候',
children: level2OfOther
});
}
return finalTree;
}
defineExpose({ getList, getDetail, handleSaveDiagnosis }); defineExpose({ getList, getDetail, handleSaveDiagnosis });
</script> </script>

View File

@@ -1137,18 +1137,28 @@ const onActionEdit = async (type) => {
getTemRequset(); getTemRequset();
}; };
// 根据code获取文字 // 根据code获取文字
// 体温(type=1): code 1-5=测量类型(腋温~肛温,不替换值), code 6-8=特殊状态(拒测/外出/请假)
// 呼吸(type=2): code 1-6=呼吸类型(平稳~抑制呼吸,不替换值), code 7=@
// 血压(type=3): code 1-3=特殊状态(拒测/外出/请假)
// 体重/腹围/身高(type=4/5/6): code 1-2=特殊状态(卧床/未测)
// 小便(type=7): code 1-2=特殊符号(※/C)
// 大便(type=8): code 1-3=特殊符号(※/☆/人工肛门)
const selectContent = (code, type) => { const selectContent = (code, type) => {
let title = ''; let title = '';
if (type == 1) { if (type == 1) {
if (code == 1) { // 体温: code 1-5 为测量类型,不替换体温值
if (code >= 1 && code <= 5) {
title = '';
} else if (code == 6) {
title = '拒测'; title = '拒测';
} else if (code == 2) { } else if (code == 7) {
title = '外出'; title = '外出';
} else { } else if (code == 8) {
title = '请假'; title = '请假';
} }
} else if (type == 2) { } else if (type == 2) {
if (code == 1) { // 呼吸: code 1-6 为呼吸类型,不替换呼吸值; code 7=@
if (code == 7) {
title = '@'; title = '@';
} }
} else if (type == 3) { } else if (type == 3) {
@@ -1183,25 +1193,30 @@ const selectContent = (code, type) => {
return title; return title;
}; };
const temperChange = (row, type = 0) => { const temperChange = (row, type = 0) => {
// 只有特殊状态码才替换输入值,测量类型选择不替换
const replaceValue = (field, content) => {
if (content) row[field] = content;
};
if (type == 1) { if (type == 1) {
row.temperature = selectContent(row.temperatureUnit, type); replaceValue('temperature', selectContent(row.temperatureUnit, type));
} else if (type == 2) { } else if (type == 2) {
row.breathe = selectContent(row.breatheUnit, type); replaceValue('breathe', selectContent(row.breatheUnit, type));
} else if (type == 3) { } else if (type == 3) {
row.systolicPressure = selectContent(row.pressureUnit, type); const content = selectContent(row.pressureUnit, type);
row.diastolicPressure = selectContent(row.pressureUnit, type); replaceValue('systolicPressure', content);
row.bloodPressure = selectContent(row.pressureUnit, type); replaceValue('diastolicPressure', content);
replaceValue('bloodPressure', content);
} else if (type == 4) { } else if (type == 4) {
row.weight = selectContent(row.weightUnit, type); replaceValue('weight', selectContent(row.weightUnit, type));
} else if (type == 5) { } else if (type == 5) {
row.waistCircumference = selectContent(row.waistCircumferenceUnit, type); replaceValue('waistCircumference', selectContent(row.waistCircumferenceUnit, type));
} else if (type == 6) { } else if (type == 6) {
row.height = selectContent(row.heightUnit, type); replaceValue('height', selectContent(row.heightUnit, type));
} else if (type == 7) { } else if (type == 7) {
row.urinationFrequency = selectContent(row.urinationFrequencyUnit, type); replaceValue('urinationFrequency', selectContent(row.urinationFrequencyUnit, type));
} else if (type == 8) { } else if (type == 8) {
console.log('uuuuuu=========>', row.stoolFrequencyUnit); console.log('uuuuuu=========>', row.stoolFrequencyUnit);
row.stoolFrequency = selectContent(row.stoolFrequencyUnit, type); replaceValue('stoolFrequency', selectContent(row.stoolFrequencyUnit, type));
} }
}; };
// 正则只能输入数字(包含小数) // 正则只能输入数字(包含小数)